﻿/*
写在前面的感谢，此软件虽然是本人独立开发，但是参考以下各位大神的作品：
1、老版的D2Loader.exe
2、原版的game.exe
3、D2SE
4、Necrolis发在https://d2mods.info/forum/viewtopic.php?t=62807的精华帖子
5、d2mods.info的诸多大神的帖子
6、https://github.com/jankowskib/d2console.git
7、特别鸣谢：CaiMiao大佬对软件DPI等方面提出的改善意见
8、还有许许多多无名大佬的工作，都是我完成这个软件的基础，这里无法一一列举了，全都表示衷心的感谢和佩服！
*/

#include <windows.h>
#include <stdio.h>
#include <direct.h>
#include <io.h>
#include <atlconv.h>
#include <iostream>
#include "D2LoaderVersion.h"
#include "common.h"
#include <TlHelp32.h>

//如果需要隐藏DOS窗口的话，就放开下面这句
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")



#define D2TRUE TRUE
#define D2FALSE FALSE

#define PATCH_FINISH    0xFFFFFFFF
#define PATCH_JMP               0x000000E9
#define PATCH_CALL              0x000000E8
#define PATCH_RETN              0x000000C3
#define PATCH_RETN4             0x000004C2
#define PATCH_RETN8             0x000008C2
#define PATCH_RETN0C            0x00000CC2
#define PATCH_RETN10            0x000010C2
#define PATCH_RETN14            0x000014C2
#define PATCH_RETN18            0x000018C2
#define PATCH_RETN1C            0x00001CC2
#define PATCH_NOPBLOCK          0x90909090

typedef struct 
{
    DWORD dwAddress;
    DWORD dwData;
    BOOL boolRelative;
    size_t iPatchSize;
} DLLPatchStrc;
static BOOL D2Loader_ApplyPatch(void* hGame, const DWORD dwBaseAddress, const DLLPatchStrc* pstPatch);

typedef BOOL (* type_pfnCallback)();

enum VideoMode
{
    gdi = 1,
    software = 2,
    ddraw = 3,
    glide = 4,
    opengl = 5, //UNUSED
    d3d = 6,
    rave = 7 //UNUSED
};

enum GameRes
{
    res_640x480 = 0,
    res_800x600 = 2
};

enum GameMode
{
    none = 0x0,
    client = 0x1,
    server = 0x2,
    multiplayer = 0x3,
    launcher = 0x4,
    expand = 0x5
};

/*
下面的ST_CLIENT_DATA是1.13c/1.13d的初始化结构体定义，也是最新最完整的，我们这里用这个结构体来保存命令行参数
在后面，根据具体的游戏版本号，再重新分配并复制这个结构体的内容
不同版本之间结构体的对应关系，可以用winhex打开对应版本的game.exe，查看二进制代码，搜索NOSOUND字符串，就可以跳转到结构体的定义处，比如下面：
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0000CBB0            4E 4F 53 4F 55  4E 44 00 00 00 00 00 00      NOSOUND      
0000CBC0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 6E 73                 ns
0000CBD0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
0000CBE0   00 00 00 00 00 00 00 00  00 00 00 00 1C 02 00 00                   
0000CBF0   00 00 00 00                                            


Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0000C550                                        4E 4F 53 4F               NOSO
0000C560   55 4E 44 00 00 00 00 00  00 00 00 00 6E 73 00 00   UND         ns  
0000C570   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   
0000C580   1B 02 00 00 00 00 00 00                                    
上面是1.13c的game.exe，下面是1.12a的game.exe
从上面的对比可以看出，在1.13c里nosound这个选项的偏移为021C，而在1.12a里则是021B，据此就可以设定主要常用参数的偏移，从而定义出正确的结构体了
*/
//size must be 0x3C8
#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE fix_aspect_ratio;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    BYTE sound_background;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_112;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_111;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE teen;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    BYTE build;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_110;
#pragma pack(pop)

#pragma pack(push, 1)
typedef struct
{
    DWORD expansion;
    BYTE window_mode;
    BYTE glide_mode;
    BYTE opengl_mode;
    BYTE rave_mode;
    BYTE d3d_mode;
    BYTE perspective;
    BYTE low_quality;
    DWORD gamma;
    BYTE vsync;
    DWORD frame_rate;
    char acPadding6[4];
    WORD join_id;
    char game_name[24];
    char game_ip[24];
    char bnet_ip[24];
    char mcp_ip[24];
    DWORD unk1;
    BYTE no_pk;
    BYTE open_c;
    BYTE amazon;
    BYTE paladin;
    BYTE sorceress;
    BYTE necromancer;
    BYTE barbarian;
    BYTE dru;
    BYTE asn;
    BYTE invincible;
    char account_name[48];
    char player_name[24];
    char realm_name[27];
    char acPadding[0xFD];
    WORD c_temp;
    WORD char_flags;
    BYTE no_monsters;
    DWORD monster_class;
    BYTE monster_info;
    DWORD monster_debug;
    BYTE item_rare;
    BYTE item_unique;
    BYTE bPadding[2];
    DWORD act;
    BYTE no_preload;
    BYTE direct;
    BYTE low_end;
    BYTE no_gfx_compress;
    DWORD arena;
    char acPadding2[6];
    type_pfnCallback mpq_callback;
    BYTE txt;
    BYTE log;
    BYTE msg_log;
    BYTE safe_mode;
    BYTE no_save;
    DWORD seed;
    BYTE cheats;
    BYTE no_sound;
    BYTE quests;
    BYTE unk4;
    void* bnet_callbacks;
    char acPadding3[0x1C];
    char game_pass[24];
    char acPadding4[0x100];
    BYTE skip_to_bnet;
    BYTE unk5;
    char acPadding5[0x6B];
    WORD unk6;
} ST_CLIENT_DATA_109;
#pragma pack(pop)

typedef BOOL (__stdcall *type_pfnStormRegSaveString)(char *keyname, char *valuename, BYTE flags, char *string);
typedef DWORD (__stdcall *type_pfnStormRegLoadString)(const char* keyname, const char* valuename, int a3, char* buffer, size_t buffersize);
typedef DWORD (__stdcall *type_pfnStormRegLoadValue)(const char* keyname, const char* valuename, int a3, int* value);
typedef DWORD (__stdcall *type_pfnStormOpenArchive)(LPCSTR lpFileName, DWORD dwPriority, DWORD dwFlags, HANDLE* hMPQ);
typedef DWORD (__stdcall *type_pfnStormCloseArchive)(HANDLE hMPQ);
typedef int (__fastcall* type_pfnStormSprintf)(void*, DWORD, const char*, ...);
typedef void (__fastcall *type_pfnFogSetLogPrefix)(const char* szPrefix);
typedef void (__fastcall *type_pfnFogSetErrorHandler)(const char* szApp, void* fnCallback, const char* szVersion, BOOL bAddInfo);
typedef void (__fastcall *type_pfnFogSetFileOptions)(BOOL bDirect, BOOL bQuickSeek);
typedef void (__fastcall *type_pfnFogSetAsyncData)(BOOL bState, DWORD dwUnknown);
typedef void (__fastcall *type_pfnFogFreeAsyncData)();
typedef BOOL (__fastcall *type_pfnIsErrorState)();
typedef void (__fastcall *type_pfnFogInit)();
typedef BOOL (__fastcall *type_pfnIsErrorState)();
typedef void (__fastcall *type_pfnErrorRescue)();
typedef void (__stdcall *type_pfnFreePools)(void* pParams);
typedef void (__stdcall *type_pfnSetServerParams)(void* pParams);
typedef BOOL(__fastcall *type_pfnFogIsExpansion)();
typedef BOOL (__stdcall *type_pfnD2WinLoadMPQs)();
typedef BOOL (__fastcall *type_pfnD2WinLoadExpansionMPQs)(void*, void*, BOOL, void*);
typedef void (__stdcall *type_pfnD2WinUnloadMPQs)();
typedef BOOL (__stdcall *type_pfnD2WinInitGfx)(HINSTANCE hInstance, int nDriver, BOOL bWindowed, BOOL bGFXCompress);
typedef BOOL (__stdcall *type_pfnD2WinDeinitGFX)();
typedef BOOL (__stdcall *type_pfnD2WinCreateWindow)(BOOL bWindowed, int ResoulutionMode);
typedef DWORD (__fastcall *type_pfnD2LaunchCall)(void *pvParam);
typedef void* (__stdcall *type_pfnD2GetHwnd)();
typedef void (__stdcall *type_pfnD2GfxRelease)();
typedef DWORD (__stdcall *type_pfnD2LangGetUsedLanguage)();
typedef void (__fastcall *type_pfnD2SoundInit)(BOOL bExpansion, BOOL bSoundBackground);
typedef DWORD (__fastcall *type_pfnD2Common10097)();
typedef void (__fastcall *type_pfnD2Fog10082)();
typedef void (__stdcall *type_pfnD2gfx10068)(DWORD);
typedef void (__stdcall *type_pfnD2gfx10071)(DWORD);
typedef int (__stdcall *type_pfnStorm426)(const char *, const char *, DWORD, DWORD);
typedef HWND (__stdcall *type_pfnGetHwnd)();

static type_pfnStormRegSaveString m_pfnStormRegSaveString = NULL;
static type_pfnStormRegLoadString m_pfnStormRegLoadString = NULL;
static type_pfnStormRegLoadValue m_pfnStormRegLoadValue = NULL;
static type_pfnStormOpenArchive m_pfnStormOpenArchive = NULL;
static type_pfnStormCloseArchive m_pfnStormCloseArchive = NULL;
static type_pfnStormSprintf m_pfnStormSprintf = NULL;
static type_pfnFogSetLogPrefix m_pfnFogSetLogPrefix = NULL;         //FOG_InitLogManager
static type_pfnFogSetErrorHandler m_pfnFogSetErrorHandler = NULL;   //FOG_InitSystem
static type_pfnFogSetFileOptions m_pfnFogSetFileOptions = NULL;     //FOG_SetWorkingDirectory
static type_pfnFogSetAsyncData m_pfnFogSetAsyncData = NULL;         //FOG_InitAsyncData
static type_pfnFogInit m_pfnFogInit = NULL;                         //FOG_InitMaskTables
static type_pfnFogIsExpansion m_pfnFogIsExpansion = NULL;           //FOG_CheckExpansion
static type_pfnIsErrorState m_pfnIsErrorState = NULL;
static type_pfnErrorRescue m_pfnErrorRescue = NULL;
static type_pfnFogFreeAsyncData m_pfnFogFreeAsyncData = NULL;       //FOG_CloseAsyncData
static type_pfnFreePools m_pfnFreePools = NULL;                     //FOG_CloseSystem
static type_pfnSetServerParams m_pfnSetServerParams = NULL;
static type_pfnD2WinLoadMPQs m_pfnD2WinLoadMPQs = NULL;             //D2WIN_LoadArchives
static type_pfnD2WinLoadExpansionMPQs m_pfnD2WinLoadExpansionMPQs = NULL;//D2WIN_LoadExpansionArchives(D2WIN_PlayDiscMessage, D2WIN_ExpansionDiscMessage, 0, pCMD)
static type_pfnD2WinUnloadMPQs m_pfnD2WinUnloadMPQs = NULL;
static type_pfnD2WinInitGfx m_pfnD2WinInitGfx = NULL;   //D2WIN_CreateWindow
static type_pfnD2WinDeinitGFX m_pfnD2WinDeinitGFX = NULL;   //D2WIN_CloseSpriteCache
static type_pfnD2WinCreateWindow m_pfnD2WinCreateWindow = NULL; //D2WIN_InitSpriteCache
static type_pfnD2GetHwnd m_pfnD2GetHwnd = NULL;
static type_pfnD2GfxRelease m_pfnD2GfxRelease = NULL;       //D2GFX_DestroyWindow
static type_pfnD2GfxRelease m_pfnD2LangFree = NULL;
static type_pfnD2LangGetUsedLanguage m_pfnD2LangGetUsedLanguage = NULL;
static type_pfnD2SoundInit m_pfnD2SoundInit = NULL;         //D2SOUND_InitSoundSystem
static type_pfnD2GfxRelease m_pfnD2SoundShutdown = NULL;    //D2SOUND_CloseSoundSystem
static type_pfnD2Common10097 m_pfnD2Common10097 = NULL;     //D2COMMON_InitIgnoreList
static type_pfnD2Fog10082 m_pfnD2Fog10082 = NULL;
static type_pfnD2gfx10068 m_pfnD2GfxSetPerspective = NULL;  //D2GFX_SetPerspectiveMode
static type_pfnD2Fog10082 m_pfnD2GfxSetLowQuality = NULL;   //D2GFX_EnableLowQuality
static type_pfnD2Fog10082 m_pfnD2GfxSetEnableVsync = NULL;  //D2GFX_EnableVSync
static type_pfnD2gfx10071 m_pfnD2GfxSetGamma = NULL;        //D2GFX_SetGamma
static type_pfnD2Fog10082 m_pfnD2GfxFixAspectRatio = NULL;  //D2GFX_SetFixedAspectRatio
static type_pfnStorm426 m_pfnStormSetResolution = NULL;
static type_pfnD2Fog10082 m_pfnD2WinUninit = NULL;          //D2WIN_UnloadArchives
static type_pfnD2Fog10082 m_pfnD2MCPClientUninit = NULL;    //D2MCPCLIENT_CloseMCP
static type_pfnD2Fog10082 m_pfnD2Common10925 = NULL;        //D2COMMON_InitIgnoreList
static type_pfnGetHwnd m_pfnGetHwnd = NULL; //D2GetWindowHandle
static void *D2WIN_PlayDiscMessage = NULL;
static void *D2WIN_ExpansionDiscMessage = NULL;

typedef struct
{
    type_pfnD2LaunchCall pfnLaunch;
    DWORD dwUnknown;
} ST_LAUNCH_CALLBACK;

typedef ST_LAUNCH_CALLBACK* (__fastcall *type_pfnD2LaunchGetCb)();
static type_pfnD2LaunchGetCb m_pfnD2LaunchGetCb = NULL;
static type_pfnD2LaunchGetCb m_pfnD2ClientGetCb = NULL;
static type_pfnD2LaunchGetCb m_pfnD2MultiGetCb = NULL;

static int m_iGameVersion = -1;
static char m_acRunningPath[D2LOADER_MAXPATH] = {0};

static const char* m_pcBoxName = "D2Loader";
static char m_acLanguageMpq[D2LOADER_MAXPATH] = {0};
static char m_acGameTitle[D2LOADER_MAXPATH] = {0};
static char m_acHackScriptPre0[D2LOADER_MAXPATH] = {0};
static char m_acHackScriptPre[D2LOADER_MAXPATH] = {0};
static char m_acHackScript0[D2LOADER_MAXPATH] = {0};
static char m_acHackScript[D2LOADER_MAXPATH] = {0};
static char m_acModPath[D2LOADER_MAXPATH] = {0};
static char m_aacExtendMpq[MAX_EXTEND_MPQ][D2LOADER_MAXPATH] = {0};
static DWORD m_dwExtendMpq = 0;
static char m_aacExtendPlugin[MAX_EXTEND_PLUGIN][D2LOADER_MAXPATH] = {0};
static DWORD m_dwExtendPlugin = 0;
static char m_aacGlobalMpqPath[MAX_EXTEND_MPQ][D2LOADER_MAXPATH] = {0};
static DWORD m_dwGlobalMpqPath = 0;
static char m_aacGlobalDllPath[MAX_EXTEND_PLUGIN][D2LOADER_MAXPATH] = {0};
static DWORD m_dwGlobalDllPath = 0;
static BOOL m_boolDepFix = FALSE;
static BOOL m_boolNoBorder = FALSE;
static BOOL m_boolMultiOpen = FALSE;
static BOOL m_boolTakePlugin = FALSE;
static BOOL m_boolCheckStruct = FALSE;
static BOOL m_boolXpCompatible = FALSE;
static BOOL m_boolWithConsole = FALSE;
static BOOL m_boolNoHide = FALSE;
static BOOL m_boolNoSleep = FALSE;
static BOOL m_boolD2Common = FALSE;
static BOOL m_boolShowAllDll = FALSE;
static BOOL m_boolHelpMode = FALSE;
static int m_iSpecRes = -1;
static HWND m_LoaderWndHandle = NULL;
static char m_acDDrawPlugin[D2LOADER_MAXPATH] = {0};
static char m_acD2DxPlugin[D2LOADER_MAXPATH] = {0};

static WNDPROC m_OldLongWinProc = NULL;

//dll加载顺序同样来自D2SE
static const char *m_apcKernelDll[] = {"Storm.dll", "Fog.dll", "D2Lang.dll", "D2CMP.dll", "D2Common.dll", "D2Net.dll", "D2Game.dll", "D2gfx.dll", "D2Sound.dll", "ijl11.dll", "D2Win.dll", "D2MCPClient.dll", "Bnclient.dll", "D2Launch.dll", "D2Client.dll", "D2Multi.dll", "binkw32.dll", "SmackW32.dll", "D2Gdi.dll", "D2DDraw.dll", "D2Direct3D.dll", "glide3x.dll", "D2Glide.dll"};

static void D2Loader_PatchModPath();

static void D2Critical_Callback()
{
    msgBox(m_pcBoxName, MB_OK | MB_ICONASTERISK, "An critical error occured!..");
    exit(1);
}

static BOOL Proc_new()
{
    return D2TRUE;
}

static char* CP_stristr(const char *pcString1, const char *pcString2)
{
    char *pCompareStart = (char *)pcString1;
    char *pCursor_S1, *pCursor_S2;
    char cSrc, cDst;

    // If there is a null source string - this is a "no match"

    if (!pcString1)
    {
        return NULL;
    }

    // Null length string 2 - this is a "no match"
    if (!*pcString2)
    {
        return NULL;
    }

    // Search from every start pos in the source string
    while (*pCompareStart)
    {
        pCursor_S1 = pCompareStart;
        pCursor_S2 = (char *)pcString2;

        // Scan both string

        while (*pCursor_S1 && *pCursor_S2)
        {
            cSrc = *pCursor_S1;
            cDst = *pCursor_S2;

            // Correct case

            if ((cSrc >= 'A') && (cSrc <= 'Z'))
            {
                cSrc -= ('A' - 'a');
            }

            if ((cDst >= 'A') && (cDst <= 'Z'))
            {
                cDst -= ('A' - 'a');
            }

            if (cSrc != cDst)
            {
                break;
            }

            pCursor_S1++;
            pCursor_S2++;
        }

        // If string 2 is exhausted - there is a match

        if (!*pCursor_S2)
        {
            return pCompareStart;
        }

        // Offset source and continue
        pCompareStart++;
    }

    return NULL;
}

static void D2Loader_Restore3RdLoadLibrary(const char *dll)
{
    if ( TRUE != m_boolTakePlugin )
    {
        return;
    }

    static char m_abCoreFile[0x200000] = {0};
    static char m_abModFile[0x200000] = {0};
    size_t dwCoreLen = 0, dwModLen = 0;
    size_t i, j;
    char acDllName[D2LOADER_MAXPATH] = {0};
    FILE *fpListFile = NULL;
    DLLPatchStrc astPatches[] =
    {
        {0, 0, FALSE, 0},
        {PATCH_FINISH} // this must be the last entry in the array!
    };
    DWORD module = (DWORD)GetModuleHandle(dll);

    if ( NULL == module )
    {
        return;
    }

    sprintf_s(acDllName, sizeof(acDllName), "%s\\CORES\\%s\\%s", m_acRunningPath, GetVersionString(m_iGameVersion), dll);
    if ( _access(acDllName, 0) != 0 )
    {
        return;
    }

    fpListFile = fopen(acDllName, "rb");
    if ( !fpListFile )
    {
        return;
    }

    dwCoreLen = fread(m_abCoreFile, 1, sizeof(m_abCoreFile), fpListFile);
    fclose(fpListFile);

    sprintf_s(acDllName, sizeof(acDllName), "%s\\%s", m_acModPath, dll);
    if ( _access(acDllName, 0) != 0 )
    {
        return;
    }

    fpListFile = fopen(acDllName, "rb");
    if ( !fpListFile )
    {
        return;
    }

    dwModLen = fread(m_abModFile, 1, sizeof(m_abModFile), fpListFile);
    fclose(fpListFile);

    if ( dwCoreLen != dwModLen || 0 == dwCoreLen || 0 == dwModLen )
    {
        return;
    }

    for ( i = 0; i < dwCoreLen; ++i )
    {
        if ( 0 == m_abCoreFile[i] && '.' == m_abModFile[i]
            && &m_abModFile[i] == CP_stristr(&m_abModFile[i], ".dll") )
        {
            j = i;
            while ( 0 != m_abModFile[j] && m_abCoreFile[j] != m_abModFile[j] )
            {
                j--;
            }
            j++;
            if ( 0 != m_abModFile[j] )
            {
                printf("Removing %s from %s\r\n", &m_abModFile[j], dll);
            }
            astPatches[0].dwAddress = j;
            astPatches[0].dwData = 0;
            astPatches[0].boolRelative = FALSE;
            astPatches[0].iPatchSize = i - j + strlen(".dll");
            D2Loader_ApplyPatch(GetCurrentProcess(), module, astPatches);
        }
    }

    return;
}

/*
int __stdcall sub_4018B0(int a1)
{
  if ( !a1 )
    return 0;
  if ( *(_WORD *)a1 != 23117 )
    return 0;
  if ( *(_DWORD *)(a1 + *(_DWORD *)(a1 + 60)) == 17744 )
    return a1 + *(_DWORD *)(a1 + 60);
  return 0;
}
*/
static int __stdcall sub_4018B0(int a1)
{
    if ( !a1 )
    {
        return 0;
    }

    if ( *(WORD *)a1 != 0x5A4D )
    {
        return 0;
    }

    if ( *(DWORD *)(a1 + *(DWORD *)(a1 + 60)) == 0x4550 )
    {
        return a1 + *(DWORD *)(a1 + 60);
    }

    return 0;
}

/*
int __stdcall sub_401910(int a1)
{
  int v1; // eax
  int result; // eax

  v1 = sub_4018B0(a1);
  if ( v1 )
    result = a1 + *(_DWORD *)(v1 + 40);
  else
    result = 0;
  return result;
}
*/
static int __stdcall sub_401910(int a1)
{
    int v1;
    int result;

    v1 = sub_4018B0(a1);

    if ( v1 )
    {
        result = a1 + *(DWORD *)(v1 + 40);
    }
    else
    {
        result = 0;
    }

    return result;
}

static DWORD sub_4018F0(DWORD a1)
{
    int v1;
    int result;

    v1 = sub_4018B0(a1);
    if ( v1 )
    {
        result = *(DWORD *)(v1 + 52);
    }
    else
    {
        result = 0;
    }
    return result;
}

static void PrintAllDllInfo()
{
    //EnableDebugPrivilege(1);
    DWORD dwProcessid = GetCurrentProcessId();//GetSpecifiedProcessId("D2Loader.exe");
    HANDLE hModules = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessid);
    if ( hModules != INVALID_HANDLE_VALUE )
    {
        MODULEENTRY32 entry;
        entry.dwSize = sizeof(MODULEENTRY32);
        Module32First(hModules, &entry);
        printf("=========================================\r\nAll customize dlls loaded by %s[0x%08X]:\r\n", entry.szModule, (DWORD)GetModuleHandle(entry.szModule));
        while ( Module32Next(hModules, &entry) )
        {
            if ( FALSE == m_boolShowAllDll )
            {
                if ( !_strnicmp("C:\\Windows", entry.szExePath, strlen("C:\\Windows"))
                    || !_strnicmp("C:\\Program", entry.szExePath, strlen("C:\\Program"))
                    || !_strnicmp("D:\\Windows", entry.szExePath, strlen("D:\\Windows"))
                    || !_strnicmp("D:\\Program", entry.szExePath, strlen("D:\\Program")))
                {
                    continue;
                }
            }
            printf("%s\t%s[0x%08X]\r\n", entry.szModule, entry.szExePath, (DWORD)GetModuleHandle(entry.szModule));
        }
        CloseHandle(hModules); 
    }
    //EnableDebugPrivilege(0);
}

static void PrintWhoOccupyMyAddr(DWORD dwAddr)
{
    //EnableDebugPrivilege(1);
    DWORD module = 0;
    DWORD dwModuleAddr = 0;
    char acModuleName[D2LOADER_MAXPATH] = {0};
    char acModulePath[D2LOADER_MAXPATH] = {0};

    DWORD dwProcessid = GetCurrentProcessId();//GetSpecifiedProcessId("D2Loader.exe");
    HANDLE hModules = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessid);

    if ( hModules != INVALID_HANDLE_VALUE )
    {
        MODULEENTRY32 entry;
        entry.dwSize = sizeof(MODULEENTRY32);
        Module32First(hModules, &entry);
        module = (DWORD)GetModuleHandle(entry.szModule);
        if ( module <= dwAddr && dwAddr - dwModuleAddr > dwAddr - module )
        {
            dwModuleAddr = module;
            strncpy_s(acModuleName, entry.szModule, sizeof(acModuleName) - 1);
            strncpy_s(acModulePath, entry.szExePath, sizeof(acModuleName) - 1);
        }

        while ( Module32Next(hModules, &entry) )
        {
            module = (DWORD)GetModuleHandle(entry.szModule);
            if ( module > dwAddr || dwAddr - dwModuleAddr <= dwAddr - module )
            {
                continue;
            }

            dwModuleAddr = module;
            strncpy_s(acModuleName, entry.szModule, sizeof(acModuleName) - 1);
            strncpy_s(acModulePath, entry.szExePath, sizeof(acModuleName) - 1);
        }
        CloseHandle(hModules); 
    }
    //EnableDebugPrivilege(0);

    if ( 0 != dwModuleAddr )
    {
        printf("    by %s[0x%08X]\r\n", acModulePath, dwModuleAddr);
    }
}

typedef void (__stdcall *type_pfnDllInit)();
static DWORD D2Loader_LoadLibrary(const char *dll)
{
    DWORD i;
    type_pfnDllInit pfnDllInit = NULL;
    char acDllName[D2LOADER_MAXPATH] = {0};
    const char *pcTemp = CP_stristr(dll, ".dll:");
    DWORD module, dwAddr;
    BOOL boolNeedCheck = FALSE;

    if ( NULL != pcTemp )
    {
        //文件名自带了初始化入口函数名
        pcTemp += 4;
        memcpy(acDllName, dll, (DWORD)(pcTemp - dll));
        pcTemp++;
    }
    else
    {
        strcpy_s(acDllName, sizeof(acDllName), dll);
    }

    if ( !_strnicmp(acDllName, "PlugY.dll", strlen("PlugY.dll")) )
    {
        //没有指定目录的大箱子，那就先找mod
        memset(acDllName, 0, sizeof(acDllName));
        if ( 0 != m_acModPath[0] )
        {
            sprintf_s(acDllName, sizeof(acDllName), "%s\\%s", m_acModPath, "PlugY.dll");
        }
        if ( 0 == acDllName[0] || _access(acDllName, 0) != 0 )
        {
            //mod下面没有PlugY，那就看看运行目录吧
            memset(acDllName, 0, sizeof(acDllName));
            sprintf_s(acDllName, sizeof(acDllName), "%s\\Tools\\PlugY\\%s", m_acRunningPath, "PlugY.dll");
            if ( _access(acDllName, 0) != 0 )
            {
                //运行目录也没有，那就按照D2ModCenter的目录结构回溯看看吧
                memset(acDllName, 0, sizeof(acDllName));
                sprintf_s(acDllName, sizeof(acDllName), "..\\..\\Tools\\PlugY\\%s", "PlugY.dll");
                if ( _access(acDllName, 0) != 0 )
                {
                    //D2ModCenter的目录也没有，只有随便操作系统了
                    strcpy_s(acDllName, "PlugY.dll");
                }
            }
        }
    }

#if 0
    if ( strlen(acDllName) >= strlen("PlugY.dll") && !_stricmp(&acDllName[strlen(acDllName) - strlen("PlugY.dll")], "PlugY.dll") )
    {
        module = (DWORD)GetModuleHandle("PlugY.dll");
        if ( module )
        {
            printf("free %s: 0x%08X\r\n", "PlugY.dll", module);
            pfnDllInit = (type_pfnDllInit)GetProcAddress((HMODULE)module, "_Release@0");
            if ( pfnDllInit )
            {
                //pfnDllInit(); 这里会释放所有的D2 dll，所以不能调用 
            }
            FreeLibrary((HMODULE)module);
        }
    }
    else if ( strlen(acDllName) >= strlen("d2mod.dll") && !_stricmp(&acDllName[strlen(acDllName) - strlen("d2mod.dll")], "d2mod.dll") )
    {
        module = (DWORD)GetModuleHandle("d2mod.dll");
        if ( module )
        {
            printf("free %s: 0x%08X\r\n", "d2mod.dll", module);
            pfnDllInit = (type_pfnDllInit)GetProcAddress((HMODULE)module, (LPCSTR)10001);
            if ( pfnDllInit )
            {
                pfnDllInit();
            }
            FreeLibrary((HMODULE)module);
        }
    }
    else if ( strlen(acDllName) >= strlen("BaseMod.dll") && !_stricmp(&acDllName[strlen(acDllName) - strlen("BaseMod.dll")], "BaseMod.dll") )
    {
        module = (DWORD)GetModuleHandle("BaseMod.dll");
        if ( module )
        {
            printf("free %s: 0x%08X\r\n", "BaseMod.dll", module);
            FreeLibrary((HMODULE)module);
        }
    }
    else if ( _stricmp("ntdll.dll", acDllName) )
    {
        char *pcSplit = strrchr(acDllName, '\\');
        if ( NULL == pcSplit )
        {
            pcSplit = acDllName;
        }
        else
        {
            pcSplit++;
        }

        for ( i = 0; i < sizeof(m_apcKernelDll) / sizeof(m_apcKernelDll[0]); ++i )
        {
            if ( !_stricmp(m_apcKernelDll[i], dll) )
            {
                break;
            }
        }

        module = (DWORD)GetModuleHandle(pcSplit);
        if ( i >= sizeof(m_apcKernelDll) / sizeof(m_apcKernelDll[0]) && module )
        {
            printf("free %s: 0x%08X\r\n", pcSplit, module);
            FreeLibrary((HMODULE)module);
        }
    }
#endif

    //printf("%x--%x\r\n", (DWORD)GetModuleHandle("Storm.dll"), (DWORD)GetModuleHandle("D:\\game\\D2ModCenterX\\CORES\\1.13c\\Storm.dll"));
    //带不带路径都不会影响获取到的句柄，所以无需担心重复加载的问题
    module = (DWORD)GetModuleHandle(acDllName);
    if ( NULL != module )
    {
        return module;
    }

    for ( i = 0; i < sizeof(m_apcKernelDll) / sizeof(m_apcKernelDll[0]); ++i )
    {
        if ( !_stricmp(m_apcKernelDll[i], dll) )
        {
            break;
        }
    }

    if ( i < sizeof(m_apcKernelDll) / sizeof(m_apcKernelDll[0]) )
    {
        //D2内部dll，非第三方插件
        memset(acDllName, 0, sizeof(acDllName));
        if ( 0 != m_acModPath[0] )
        {
            sprintf_s(acDllName, sizeof(acDllName), "%s\\%s", m_acModPath, dll);
            if ( _access(acDllName, 0) != 0 )
            {
                memset(acDllName, 0, sizeof(acDllName));
            }
            else
            {
                boolNeedCheck = TRUE;
            }
        }

        //mod path里没有，就到core目录里去找，理论上那里肯定有
        if ( 0 == acDllName[0] )
        {
            sprintf_s(acDllName, sizeof(acDllName), "%s\\CORES\\%s\\%s", m_acRunningPath, GetVersionString(m_iGameVersion), dll);
            if ( _access(acDllName, 0) != 0 )
            {
                //如果也没有，那就按照原始名称，让操作系统自己去找了
                strcpy_s(acDllName, sizeof(acDllName), dll);
            }
        }
    }

    printf("loading %s", acDllName);
    module = (DWORD)LoadLibraryEx(acDllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
    if ( NULL == module )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONASTERISK, "Load %s failed[%d]!", acDllName, GetLastError());
        exit(1);
    }
    printf(": 0x%08X\r\n", (DWORD)GetModuleHandle(acDllName));

    dwAddr = sub_4018F0(module);
    if ( module != dwAddr )
    {
        printf("ImageBase of %s changed from 0x%08X to 0x%08X\r\n", acDllName, dwAddr, module);
        PrintWhoOccupyMyAddr(dwAddr);
    }

    if ( TRUE == boolNeedCheck )
    {
        D2Loader_Restore3RdLoadLibrary(dll); //插件开发者有可能会在D2内部dll里直接加载他们的插件，这是不符合规范的，统一放到大箱子后面去
    }

    if ( strlen(acDllName) >= strlen("PlugY.dll") 
        && !_stricmp(&acDllName[strlen(acDllName) - strlen("PlugY.dll")], "PlugY.dll")
        && NULL == pcTemp )
    {
        pcTemp = "_Init@4";    //_Release@0是Plugy的释放函数
    }
    else if ( strlen(acDllName) >= strlen("d2mod.dll") 
        && !_stricmp(&acDllName[strlen(acDllName) - strlen("d2mod.dll")], "d2mod.dll")
        && NULL == pcTemp )
    {
        pcTemp = "#10000";  //10001是d2mod的释放函数
    }
    else if ( strlen(acDllName) >= strlen("D2SE_Utility.dll") 
        && !_stricmp(&acDllName[strlen(acDllName) - strlen("D2SE_Utility.dll")], "D2SE_Utility.dll")
        && NULL == pcTemp )
    {
        pcTemp = "#10000";  //10001是D2SE_Utility的释放函数
    }

    if ( NULL != pcTemp )
    {
        //有指定入口函数
        if ( '#' == *pcTemp )
        {
            pfnDllInit = (type_pfnDllInit)GetProcAddress((HMODULE)module, (LPCSTR)atol(pcTemp+1));
        }
        else
        {
            pfnDllInit = (type_pfnDllInit)GetProcAddress((HMODULE)module, pcTemp);
        }
        if ( NULL != pfnDllInit )
        {
            printf("call %s\r\n", pcTemp);
            if ( strlen(acDllName) >= strlen("PlugY.dll") && !_stricmp(&acDllName[strlen(acDllName) - strlen("PlugY.dll")], "PlugY.dll") )
            {
                ((void (__stdcall *)(int))pfnDllInit)(1);
            }
            else
            {
                pfnDllInit();
            }
        }
        else
        {
            printf("not found entry(%s)\r\n", pcTemp);
        }
    }

    return module;
}

static DWORD GetDllOffset2(const char* dll, const char* pcName)
{
    HMODULE module = (HMODULE)D2Loader_LoadLibrary(dll);
    DWORD locatedAddress = (DWORD)GetProcAddress(module, (LPCSTR)pcName);
    return locatedAddress;
}

static DWORD GetDllOffset(const char* dll, int offset)
{
    return GetDllOffset2(dll, (LPCSTR)offset);
}

static void LoadAllLibraries(ST_CLIENT_DATA *pstClientData, DWORD dwVideoMode)
{
    void (__stdcall *pfnCallback)(HMODULE, signed int, signed int) = NULL;
    HMODULE hStormHandle = NULL;
    DWORD i;
    char acDllName[D2LOADER_MAXPATH] = {0};

    if ( (ddraw == dwVideoMode || gdi == dwVideoMode) && 0 != m_acDDrawPlugin[0] )
    {
        //cnc-ddraw在这里就要提前加载了，否则后面会找到系统目录里去
        if ( NULL != strchr(m_acDDrawPlugin, '\\') )
        {
            //指定路径，直接加载
            D2Loader_LoadLibrary(m_acDDrawPlugin);
        }
        else
        {
            memset(acDllName, 0, sizeof(acDllName));
            if ( 0 != m_acModPath[0] )
            {
                sprintf_s(acDllName, sizeof(acDllName), "%s\\%s", m_acModPath, "ddraw.dll");
            }
            if ( 0 == acDllName[0] || _access(acDllName, 0) != 0 )
            {
                //mod下面没有ddraw，那就看看根目录吧
                memset(acDllName, 0, sizeof(acDllName));
                sprintf_s(acDllName, sizeof(acDllName), "%s\\%s", m_acRunningPath, "\\Tools\\cnc-ddraw\\ddraw.dll");
                if ( _access(acDllName, 0) != 0 )
                {
                    //根目录也没有，只有随便操作系统了
                }
                else
                {
                    D2Loader_LoadLibrary(acDllName);
                }
            }
            else
            {
                D2Loader_LoadLibrary(acDllName);
            }
        }
    }

    if ( TRUE == m_boolD2Common )
    {
        //因为某些MOD用到了特殊基址的D2Common.dll，一旦基址被占用就无法正常运行了，所以提前加载
        D2Loader_LoadLibrary("D2Common.dll");
    }

    //Storm.dll特殊处理，解决第三方loader的兼容问题，from D2SE，Thanks!
    /*
    HMODULE __stdcall sub_40BA50(LPCSTR lpLibFileName)
    {
      return LoadLibraryExA(lpLibFileName, 0, 8u);
    }

    HMODULE v16; // eax
    HMODULE v17; // esi
    void (__stdcall *v18)(HMODULE, signed int, signed int); // ebx
    v16 = sub_40BA50((LPCSTR)&PathName);
    v17 = v16;
    if ( !v16 )
      return 0;
    v18 = (void (__stdcall *)(HMODULE, signed int, signed int))sub_401910((int)v16);
    v18(v17, 0, 268435712);
    v18(v17, 1, 268435712);
    */
    for ( i = 0; i < sizeof(m_apcKernelDll) / sizeof(m_apcKernelDll[0]); i++ )
    {
        if ( !_stricmp(m_apcKernelDll[i], "D2Multi.dll") )
        {
            //要放到后面去加载
            continue;
        }

        if ( !_stricmp(m_apcKernelDll[i], "glide3x.dll") && 0 != m_acD2DxPlugin[0] )
        {
            if ( NULL != strchr(m_acD2DxPlugin, '\\') )
            {
                //指定路径，直接加载
                strcpy_s(acDllName, sizeof(acDllName), m_acD2DxPlugin);
            }
            else
            {
                memset(acDllName, 0, sizeof(acDllName));
                if ( 0 != m_acModPath[0] )
                {
                    sprintf_s(acDllName, sizeof(acDllName), "%s\\%s", m_acModPath, "glide3x.dll");
                }
                if ( 0 == acDllName[0] || _access(acDllName, 0) != 0 )
                {
                    //mod下面没有glide3x，那就看看根目录吧
                    memset(acDllName, 0, sizeof(acDllName));
                    sprintf_s(acDllName, sizeof(acDllName), "%s\\%s", m_acRunningPath, "\\Tools\\d2dx\\glide3x.dll");
                    if ( _access(acDllName, 0) != 0 )
                    {
                        strcpy_s(acDllName, sizeof(acDllName), m_apcKernelDll[i]);
                    }
                }
            }
        }
        else
        {
            strcpy_s(acDllName, sizeof(acDllName), m_apcKernelDll[i]);
        }

        hStormHandle = (HMODULE)D2Loader_LoadLibrary(acDllName);

        if ( NULL != hStormHandle && !_stricmp(m_apcKernelDll[i], "Storm.dll") )
        {
            pfnCallback = (void (__stdcall *)(HMODULE, signed int, signed int))sub_401910((DWORD)hStormHandle);
            if ( NULL != pfnCallback )
            {
                pfnCallback(hStormHandle, 0, 0x10000100);
                pfnCallback(hStormHandle, 1, 0x10000100);
            }
        }
    }
}

static void D2Loader_CheckDllImageBase()
{
    DWORD i;
    DWORD module;
    DWORD dwAddr;

    //来自D2SE
    for ( i = 0; i < sizeof(m_apcKernelDll) / sizeof(m_apcKernelDll[0]); i++ )
    {
        module = (DWORD)GetModuleHandle(m_apcKernelDll[i]);
        if ( NULL == module )
        {
            continue;
        }

        dwAddr = sub_4018F0(module);
        if ( module != dwAddr )
        {
            printf("ImageBase of %s changed from 0x%08X to 0x%08X\r\n", m_apcKernelDll[i], dwAddr, module);
        }
    }
}

static BOOL D2Loader_InitFuncPtr()
{
    switch ( m_iGameVersion )
    {
        case V109d:
            m_pfnStormRegSaveString = (type_pfnStormRegSaveString)GetDllOffset("Storm.dll", 425);
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10037);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10171);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10000);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10001);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10000);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10001);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10002);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10001);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10011);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10015);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10018);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10036);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10001);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            m_pfnD2GfxSetEnableVsync = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10065);
            D2WIN_PlayDiscMessage = (void *)GetDllOffset("D2Win.dll", 10152);
            D2WIN_ExpansionDiscMessage = (void *)GetDllOffset("D2Win.dll", 10140);
            break;

        case V110f:
            m_pfnStormRegSaveString = (type_pfnStormRegSaveString)GetDllOffset("Storm.dll", 425);
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10037);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10171);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10000);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10001);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10000);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10001);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10002);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10001);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10011);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10015);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10018);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10036);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10001);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10027);
            m_pfnD2GfxSetEnableVsync = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10065);
            D2WIN_PlayDiscMessage = (void *)GetDllOffset("D2Win.dll", 10152);
            D2WIN_ExpansionDiscMessage = (void *)GetDllOffset("D2Win.dll", 10140);
            break;

        case V111b:
            m_pfnStormRegSaveString = (type_pfnStormRegSaveString)GetDllOffset("Storm.dll", 425);
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10030);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10051);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10089);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10100);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10050);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10044);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10182);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10034);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10177);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10026);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10061);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10055);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10003);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10032);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10039);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10022);
            m_pfnD2GfxSetEnableVsync = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10065);
            D2WIN_PlayDiscMessage = (void *)GetDllOffset("D2Win.dll", 10152);
            D2WIN_ExpansionDiscMessage = (void *)GetDllOffset("D2Win.dll", 10140);
            break;

        case V112a:
            m_pfnStormRegSaveString = (type_pfnStormRegSaveString)GetDllOffset("Storm.dll", 425);
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10059);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10073);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10188);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10069);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10109);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10078);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10036);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10022);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10008);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10032);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10002);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10121);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10010);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10045);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10024);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10080);
            m_pfnD2GfxSetEnableVsync = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10065);
            D2WIN_PlayDiscMessage = (void *)GetDllOffset("D2Win.dll", 10152);
            D2WIN_ExpansionDiscMessage = (void *)GetDllOffset("D2Win.dll", 10140);
            break;

        case V113c:
            m_pfnStormRegSaveString = (type_pfnStormRegSaveString)GetDllOffset("Storm.dll", 425);
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10086);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10005);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10142);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10052);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10002);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10031);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10032);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10084);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10088);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10007);
            m_pfnD2LangFree = (type_pfnD2GfxRelease)GetDllOffset("D2Lang.dll", 10000);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnIsErrorState = (type_pfnIsErrorState)GetDllOffset("Fog.dll", 10039);
            m_pfnErrorRescue = (type_pfnErrorRescue)GetDllOffset("Fog.dll", 10040);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10081);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10053);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10034);
            m_pfnD2GfxFixAspectRatio = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10066);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10158);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10018);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10048);
            m_pfnD2GfxSetEnableVsync = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10065);
            D2WIN_PlayDiscMessage = (void *)GetDllOffset("D2Win.dll", 10152);
            D2WIN_ExpansionDiscMessage = (void *)GetDllOffset("D2Win.dll", 10140);
            break;

        case V113d:
            m_pfnStormRegSaveString = (type_pfnStormRegSaveString)GetDllOffset("Storm.dll", 425);
            m_pfnStormRegLoadString = (type_pfnStormRegLoadString)GetDllOffset("Storm.dll", 422);
            m_pfnStormSprintf = (type_pfnStormSprintf)GetDllOffset("Storm.dll", 578);
            m_pfnFogSetLogPrefix = (type_pfnFogSetLogPrefix)GetDllOffset("Fog.dll", 10021);
            m_pfnFogSetErrorHandler = (type_pfnFogSetErrorHandler)GetDllOffset("Fog.dll", 10019);
            m_pfnFogSetFileOptions = (type_pfnFogSetFileOptions)GetDllOffset("Fog.dll", 10101);
            m_pfnFogSetAsyncData = (type_pfnFogSetAsyncData)GetDllOffset("Fog.dll", 10089);
            m_pfnFogInit = (type_pfnFogInit)GetDllOffset("Fog.dll", 10218);
            m_pfnD2WinLoadMPQs = (type_pfnD2WinLoadMPQs)GetDllOffset("D2Win.dll", 10174);
            m_pfnD2WinLoadExpansionMPQs = (type_pfnD2WinLoadExpansionMPQs)GetDllOffset("D2Win.dll", 10072);
            m_pfnFogIsExpansion = (type_pfnFogIsExpansion)GetDllOffset("Fog.dll", 10227);
            m_pfnD2WinInitGfx = (type_pfnD2WinInitGfx)GetDllOffset("D2Win.dll", 10071);
            m_pfnD2WinCreateWindow = (type_pfnD2WinCreateWindow)GetDllOffset("D2Win.dll", 10129);
            m_pfnD2SoundInit = (type_pfnD2SoundInit)GetDllOffset("D2Sound.dll", 10023);
            m_pfnD2SoundShutdown = (type_pfnD2GfxRelease)GetDllOffset("D2Sound.dll", 10024);
            m_pfnD2WinDeinitGFX = (type_pfnD2WinDeinitGFX)GetDllOffset("D2Win.dll", 10132);
            m_pfnD2GfxRelease = (type_pfnD2GfxRelease)GetDllOffset("D2Gfx.dll", 10050);
            m_pfnD2WinUnloadMPQs = (type_pfnD2WinUnloadMPQs)GetDllOffset("D2Win.dll", 10079);
            m_pfnD2GetHwnd = (type_pfnD2GetHwnd)GetDllOffset("D2Gfx.dll", 10007);
            m_pfnD2LangFree = (type_pfnD2GfxRelease)GetDllOffset("D2Lang.dll", 10000);
            m_pfnStormRegLoadValue = (type_pfnStormRegLoadValue)GetDllOffset("Storm.dll", 423);
            m_pfnStormOpenArchive = (type_pfnStormOpenArchive)GetDllOffset("Storm.dll", 266);
            m_pfnStormCloseArchive = (type_pfnStormCloseArchive)GetDllOffset("Storm.dll", 252);
            m_pfnIsErrorState = (type_pfnIsErrorState)GetDllOffset("Fog.dll", 10039);
            m_pfnErrorRescue = (type_pfnErrorRescue)GetDllOffset("Fog.dll", 10040);
            m_pfnFogFreeAsyncData = (type_pfnFogFreeAsyncData)GetDllOffset("Fog.dll", 10090);
            m_pfnFreePools = (type_pfnFreePools)GetDllOffset("Fog.dll", 10143);
            m_pfnSetServerParams = (type_pfnSetServerParams)GetDllOffset("Fog.dll", 10185);
            m_pfnD2Fog10082 = (type_pfnD2Fog10082)GetDllOffset("Fog.dll", 10082);
            m_pfnD2Common10097 = (type_pfnD2Common10097)GetDllOffset("D2Common.dll", 10097);
            m_pfnD2GfxSetPerspective = (type_pfnD2gfx10068)GetDllOffset("D2Gfx.dll", 10068);
            m_pfnD2GfxSetLowQuality = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10008);
            m_pfnD2GfxSetGamma = (type_pfnD2gfx10071)GetDllOffset("D2Gfx.dll", 10071);
            m_pfnD2GfxFixAspectRatio = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10049);
            m_pfnStormSetResolution = (type_pfnStorm426)GetDllOffset("Storm.dll", 426);
            m_pfnD2WinUninit = (type_pfnD2Fog10082)GetDllOffset("D2Win.dll", 10058);
            m_pfnD2MCPClientUninit = (type_pfnD2Fog10082)GetDllOffset("D2MCPClient.dll", 10006);
            m_pfnD2Common10925 = (type_pfnD2Fog10082)GetDllOffset("D2Common.dll", 10925);
            m_pfnGetHwnd = (type_pfnGetHwnd)GetDllOffset("D2Gfx.dll", 10007);
            m_pfnD2GfxSetEnableVsync = (type_pfnD2Fog10082)GetDllOffset("D2Gfx.dll", 10065);
            D2WIN_PlayDiscMessage = (void *)GetDllOffset("D2Win.dll", 10152);
            D2WIN_ExpansionDiscMessage = (void *)GetDllOffset("D2Win.dll", 10140);
            break;
    
        default:
            break;
    }

    return TRUE;
}

static void D2Loader_AppendDllPath(char *pcDllPath)
{
    static TCHAR acPath[32768] = {0};
    DWORD dwLength = 0;

    memset(acPath, 0, sizeof(acPath));
    dwLength = GetEnvironmentVariable("PATH", acPath, sizeof(acPath)/sizeof(acPath[0]));
    sprintf(&acPath[dwLength], ";%s", pcDllPath);
    SetEnvironmentVariable("PATH", acPath);
}

static void parse_command(int argc, char** argv, ST_CLIENT_DATA *pstClientData)
{
    DWORD j;

    for ( int i = 1; i < argc; ++i )
    {
        if ( !_stricmp("-help", argv[i]) || !_stricmp("-info", argv[i]) )
        {
            m_boolHelpMode = TRUE;
            m_boolWithConsole = TRUE;
        }
        else if ( !_stricmp("-ver", argv[i]) )
        {
            i++;
            m_iGameVersion = D2Loader_GetVersionId(argv[i]);
        }
        else if ( !_stricmp("-check", argv[i]) )
        {
            m_boolCheckStruct = TRUE;
            m_boolWithConsole = TRUE;
        }
        else if ( !_stricmp("-nohide", argv[i]) )
        {
            m_boolNoHide = TRUE;
        }
        else if ( !_stricmp("-nosleep", argv[i]) )
        {
            m_boolNoSleep = TRUE;
        }
        else if ( !_stricmp("-xp", argv[i]) )
        {
            m_boolXpCompatible = TRUE;
        }
        else if ( !_stricmp("-console", argv[i]) )
        {
            m_boolWithConsole = TRUE;
        }
        else if ( !_stricmp("-hackpre0", argv[i]) )
        {
            i++;
            strncpy_s(m_acHackScriptPre0, argv[i], sizeof(m_acHackScriptPre0) - 1);
        }
        else if ( !_stricmp("-hackpre", argv[i]) )
        {
            i++;
            strncpy_s(m_acHackScriptPre, argv[i], sizeof(m_acHackScriptPre) - 1);
        }
        else if ( !_stricmp("-hack0", argv[i]) )
        {
            i++;
            strncpy_s(m_acHackScript0, argv[i], sizeof(m_acHackScript0) - 1);
        }
        else if ( !_stricmp("-hack", argv[i]) )
        {
            i++;
            strncpy_s(m_acHackScript, argv[i], sizeof(m_acHackScript) - 1);
        }
        else if ( !_stricmp("-w", argv[i]) )
        {
            pstClientData->window_mode = D2TRUE;
        }
        else if ( !_stricmp("-glide", argv[i]) || !_stricmp("-3dfx", argv[i]) )
        {
            pstClientData->glide_mode = D2TRUE;
        }
        else if ( !_stricmp("-opengl", argv[i]) )
        {
            pstClientData->opengl_mode = D2TRUE;
        }
        else if ( !_stricmp("-d3d", argv[i]) )
        {
            pstClientData->d3d_mode = D2TRUE;
        }
        else if ( !_stricmp("-per", argv[i]) )
        {
            pstClientData->perspective = D2TRUE;
        }
        else if ( !_stricmp("-lq", argv[i]) )
        {
            pstClientData->low_quality = D2TRUE;
        }
        else if ( !_stricmp("-gamma", argv[i]) && i + 1 < argc )
        {
            i++;
            pstClientData->gamma = atoi(argv[i]);
        }
        else if ( !_stricmp("-vsync", argv[i]) )
        {
            pstClientData->vsync = D2TRUE;
        }
        else if ( !_stricmp("-fr", argv[i]) && i + 1 < argc )
        {
            i++;
            pstClientData->frame_rate = atoi(argv[i]);
        }
        else if ( !_stricmp("-joinid", argv[i]) && i + 1 < argc )
        {
            i++;
            pstClientData->join_id = atoi(argv[i]);
        }
        else if ( !_stricmp("-gamename", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->game_name, argv[i], sizeof(pstClientData->game_name) - 1);
        }
        else if ( !_stricmp("-bn", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->bnet_ip, argv[i], sizeof(pstClientData->bnet_ip) - 1);
        }
        else if ( !_stricmp("-mcpip", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->mcp_ip, argv[i], sizeof(pstClientData->mcp_ip) - 1);
        }
        else if ( !_stricmp("-nopk", argv[i]) )
        {
            pstClientData->no_pk = D2TRUE;
        }
        else if ( !_stricmp("-openc", argv[i]) )
        {
            pstClientData->open_c = D2TRUE;
        }
        else if ( !_stricmp("-arena", argv[i]) )
        {
            pstClientData->arena = D2TRUE;
        }
        else if ( !_stricmp("-txt", argv[i]) )
        {
            pstClientData->txt = D2TRUE;
        }
        else if ( !_stricmp("-ama", argv[i]) )
        {
            pstClientData->amazon = D2TRUE;
        }
        else if ( !_stricmp("-pal", argv[i]) )
        {
            pstClientData->paladin = D2TRUE;
        }
        else if ( !_stricmp("-sor", argv[i]) )
        {
            pstClientData->sorceress = D2TRUE;
        }
        else if ( !_stricmp("-nec", argv[i]) )
        {
            pstClientData->necromancer = D2TRUE;
        }
        else if ( !_stricmp("-bar", argv[i]) )
        {
            pstClientData->barbarian = D2TRUE;
        }
        else if ( !_stricmp("-dru", argv[i]) )
        {
            pstClientData->dru = D2TRUE;
        }
        else if ( !_stricmp("-asn", argv[i]) )
        {
            pstClientData->asn = D2TRUE;
        }
        else if ( !_stricmp("-i", argv[i]) )
        {
            pstClientData->invincible = D2TRUE;
        }
        else if ( !_stricmp("-bnacct", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->account_name, argv[i], sizeof(pstClientData->account_name) - 1);
        }
        else if ( !_stricmp("-bnpass", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->game_pass, argv[i], sizeof(pstClientData->game_pass) - 1);
        }
        else if ( !_stricmp("-name", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->player_name, argv[i], sizeof(pstClientData->player_name) - 1);
        }
        else if ( !_stricmp("-realm", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->realm_name, argv[i], sizeof(pstClientData->realm_name) - 1);
        }
        else if ( !_stricmp("-ctemp", argv[i]) && i + 1 < argc )
        {
            i++;
            pstClientData->c_temp = atoi(argv[i]);
        }
        else if ( !_stricmp("-nm", argv[i]) )
        {
            pstClientData->no_monsters = D2TRUE;
        }
        else if ( !_stricmp("-m", argv[i]) )
        {
            pstClientData->monster_class = D2TRUE;
        }
        else if ( !_stricmp("-minfo", argv[i]) )
        {
            pstClientData->monster_info = D2TRUE;
        }
        else if ( !_stricmp("-md", argv[i]) )
        {
            pstClientData->monster_debug = D2TRUE;
        }
        else if ( !_stricmp("-rare", argv[i]) )
        {
            pstClientData->item_rare = D2TRUE;
        }
        else if ( !_stricmp("-unique", argv[i]) )
        {
            pstClientData->item_unique = D2TRUE;
        }
        else if ( !_stricmp("-act", argv[i]) && i + 1 < argc )
        {
            i++;
            pstClientData->act = atoi(argv[i]);
        }
        else if ( !_stricmp("-log", argv[i]) )
        {
            pstClientData->log = D2TRUE;
        }
        else if ( !_stricmp("-msglog", argv[i]) )
        {
            pstClientData->msg_log = D2TRUE;
        }
        else if ( !_stricmp("-safe", argv[i]) )
        {
            pstClientData->safe_mode = D2TRUE;
        }
        else if ( !_stricmp("-seed", argv[i]) && i + 1 < argc )
        {
            i++;
            pstClientData->seed = atoi(argv[i]);
        }
        else if ( !_stricmp("-cheats", argv[i]) )
        {
            pstClientData->cheats = D2TRUE;
        }
        else if ( !_stricmp("-ns", argv[i]) || !_stricmp("-nosound", argv[i]) )
        {
            pstClientData->no_sound = D2TRUE;
        }
        else if ( !_stricmp("-questall", argv[i]) )
        {
            pstClientData->quests = D2TRUE;
        }
        else if ( !_stricmp("-npl", argv[i]) )
        {
            pstClientData->no_preload = D2TRUE;
        }
        else if ( !_stricmp("-direct", argv[i]) )
        {
            pstClientData->direct = D2TRUE;
        }
        else if ( !_stricmp("-lem", argv[i]) )
        {
            pstClientData->low_end = D2TRUE;
        }
        else if ( !_stricmp("-nocompress", argv[i]) )
        {
            pstClientData->no_gfx_compress = D2TRUE;
        }
        else if ( !_stricmp("-gamepass", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(pstClientData->game_pass, argv[i], sizeof(pstClientData->game_pass) - 1);
        }
        else if ( !_stricmp("-skiptobnet", argv[i]) )
        {
            pstClientData->skip_to_bnet = D2TRUE;
        }
        else if ( !_stricmp("-client", argv[i]) )
        {
        }
        else if ( !_stricmp("-server", argv[i]) )
        {
        }
        else if ( !_stricmp("-launch", argv[i]) )
        {
        }
        else if ( !_stricmp("-notitle", argv[i]) )
        {
            strncpy_s(m_acGameTitle, "notitle", sizeof(m_acGameTitle) - 1);
        }
        else if ( !_stricmp("-res800", argv[i]) )
        {
            m_iSpecRes = 1;
        }
        else if ( !_stricmp("-res640", argv[i]) )
        {
            m_iSpecRes = 0;
        }
        else if ( !_stricmp("-title", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(m_acGameTitle, argv[i], sizeof(m_acGameTitle) - 1);
        }
        else if ( !_stricmp("-locale", argv[i]) && i + 1 < argc )
        {
            sprintf_s(m_acLanguageMpq, "Language_%s\\%s.mpq", argv[i+1], argv[i+1]);
            ++i;
        }
        else if ( !_stricmp("-mpq", argv[i]) )
        {
            //指定额外的mpq，最多MAX_EXTEND_MPQ个，空格分开，可以带路径，路径有空格要用双引号，比如：-mpq Language_CHI\CHI.mpq
            while ( i + 1 < argc && '-' != argv[i+1][0] && m_dwExtendMpq < MAX_EXTEND_MPQ )
            {
                i++;
                sprintf_s(m_aacExtendMpq[m_dwExtendMpq], "%s", argv[i]);
                m_dwExtendMpq++;
            }
        }
        else if ( !_stricmp("-plugin", argv[i]) )
        {
            //指定额外的dll，最多MAX_EXTEND_PLUGIN个，空格分开，可以带路径，路径有空格要用双引号，比如：-plugin d2hackmap\d2hackmap.dll
            //另外，可以追加plugin的初始化函数，比如：-plugin PlugY.dll:_Init@4
            while ( i + 1 < argc && '-' != argv[i+1][0] && m_dwExtendPlugin < MAX_EXTEND_PLUGIN )
            {
                i++;
                sprintf_s(m_aacExtendPlugin[m_dwExtendPlugin], "%s", argv[i]);
                m_dwExtendPlugin++;
            }
        }
        else if ( !_stricmp("-mpqpath", argv[i]) && i + 1 < argc )
        {
            //追加mpqs的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠"\\"，比如：-mpqpath "d:\Diablo II D2SE"
            while ( i + 1 < argc && '-' != argv[i+1][0] && m_dwGlobalMpqPath < MAX_EXTEND_MPQ_PATH )
            {
                i++;
                sprintf_s(m_aacGlobalMpqPath[m_dwGlobalMpqPath], "%s", argv[i]);
                j = strlen(m_aacGlobalMpqPath[m_dwGlobalMpqPath]) - 1;
                while ( '\\' == m_aacGlobalMpqPath[m_dwGlobalMpqPath][j] )
                {
                    m_aacGlobalMpqPath[m_dwGlobalMpqPath][j] = 0;
                    j--;
                }
                m_dwGlobalMpqPath++;
            }
        }
        else if ( !_stricmp("-dllpath", argv[i]) )
        {
            //追加dll的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠"\\"，比如：-dllpath "d:\Diablo II D2SE" "d:\Diablo II D2SE\D2SE\CORES\1.13c"
            while ( i + 1 < argc && '-' != argv[i+1][0] && m_dwGlobalDllPath < MAX_EXTEND_PLUGIN_PATH )
            {
                i++;
                sprintf_s(m_aacGlobalDllPath[m_dwGlobalDllPath], "%s", argv[i]);
                j = strlen(m_aacGlobalDllPath[m_dwGlobalDllPath]) - 1;
                while ( '\\' == m_aacGlobalDllPath[m_dwGlobalDllPath][j] )
                {
                    m_aacGlobalDllPath[m_dwGlobalDllPath][j] = 0;
                    j--;
                }
                m_dwGlobalDllPath++;
            }
        }
        else if ( !_stricmp("-depfix", argv[i]) )
        {
            m_boolDepFix = TRUE;
        }
        else if ( !_stricmp("-noborder", argv[i]) )
        {
            m_boolNoBorder = TRUE;
        }
        else if ( !_stricmp("-multiopen", argv[i]) )
        {
            m_boolMultiOpen = TRUE;
        }
        else if (!_stricmp("-modpath", argv[i]) && i + 1 < argc)
        {
            i++;
            strncpy_s(m_acModPath, argv[i], sizeof(m_acModPath) - 1);
        }
        else if ( !_stricmp("-ddraw", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(m_acDDrawPlugin, argv[i], sizeof(m_acDDrawPlugin) - 1);
        }
        else if ( !_stricmp("-d2dx", argv[i]) && i + 1 < argc )
        {
            i++;
            strncpy_s(m_acD2DxPlugin, argv[i], sizeof(m_acD2DxPlugin) - 1);
        }
        else if ( !_stricmp("-takeplugin", argv[i]) )
        {
            m_boolTakePlugin = TRUE;
        }
        else if ( !_stricmp("-d2common", argv[i]) )
        {
            m_boolD2Common = TRUE;
        }
        else if ( !_stricmp("-showalldll", argv[i]) )
        {
            m_boolShowAllDll = TRUE;
        }
    }

    if ( 0 != m_acModPath[0] )
    {
        //SetDefaultDllDirectories(LOAD_WITH_ALTERED_SEARCH_PATH);
        D2Loader_AppendDllPath(m_acModPath);
    }
    for ( DWORD i = 0; i < m_dwGlobalDllPath; ++i )
    {
        D2Loader_AppendDllPath(m_aacGlobalDllPath[i]);
    }
    /*
    举两个例子：
    D2SE版1.13c
    D2Loader.exe -w -mpq language_chi\chi.mpq -mpqpath "d:\Diablo II D2SE" -dllpath "d:\Diablo II D2SE" "d:\Diablo II D2SE\D2se\CORES\1.13c" -plugin PlugY.dll:_Init@4 -glide -direct -txt
    或者
    D2Loader.exe -w -locale chi -mpqpath "d:\Diablo II D2SE" -dllpath "d:\Diablo II D2SE" "d:\Diablo II D2SE\D2se\CORES\1.13c" -plugin PlugY.dll:_Init@4 -glide -direct -txt
    D2SE版1.13d
    D2Loader.exe -w -mpqpath "d:\Diablo II D2SE" -direct -txt -plugin PlugY.dll:_Init@4 -glide
    */
    /*
    1.视频选项
    -exp -expansion 切换到扩展模式
    -w -window 切换到窗口模式
    -glide -glide 使用Glide显示模式
    -opengl -opengl 使用OpenGL显示模式
    -d3d -d3d 使用Direct 3D显示模式
    -rave -rave 使用Rave显示模式，仅适用于Mac
    -per -perspective 打开透视模式，仅适用于全屏非Direct Draw模式
    -lq -lowquality 低图像质量(高速度)
    -gamma -gamma 设置Gamma值为
    -vsync -vsync 打开VSync
    -fr -framerate 甚至帧速率为
    2.网络选项
    -s -serverip 设置TCP/IP游戏服务器的IP为
    -gametype -gametype 设置游戏类型为
    -joinid -joinid 设置加入游戏的ID为
    -gamename -gamename 设置游戏名为
    -bn -battlenetip 设置battle.net服务器IP为
    -mcpip -mcpip 设置mcpip服务器IP为
    -nopk -nopk 禁止PK(好像无效)
    -openc -openc 不清楚
    3.游戏选项
    -arena -arena 无效
    -difficulty -difficulty 无效
    -txt -txt 给MOD制作者，用于创建.bin文件
    4.角色选项
    -ama -ama 设置角色类型为ama
    -pal -pal 设置角色类型为pal
    -sor -sor 设置角色类型为sor
    -nec -nec 设置角色类型为nec
    -bar -bar 设置角色类型为bar
    -dru -dru 设置角色类型为dru
    -asn -asn 设置角色类型为asn
    -i -invincible 隐形？(好像无效)
    -bnacct -bnacct 设置battle.net账号名字为
    -bnpass -bnpass 设置battle.net密码为
    -name -name 设置battle.net角色名字为
    -realm -realm 设置battle.net服务器(Realm)名字为
    -ctemp -ctemp 在arena模式使用第个角色的模板
    5.怪物选项
    -nm -nomonster 无怪物？(无效)
    -m -monsterclass 不清楚
    -minfo -monsterinfo 显示怪物信息？(无效)
    -md -monsterdebug 不清楚
    6.物品选项
    -rare -rare 全稀有( Rare )物品？(无效)
    -unique -unique 全独特(Unique)物品？(无效)
    7.界面选项
    -act -act 设置初始位置为第幕
    8.Debug选项
    -log -log 激活log(无效)
    -msglog -msglog 激活msglog
    -safe -safemode 安全模式？
    -seed -seed 设置地图种子(ala5:可理解为地图ID)为
    -cheats -cheats 不清初
    -ns -nosound 无声模式
    -questall -questall 不清楚
    9.文件输入输出选项
    -npl -nopreload 不预读取游戏文件
    -direct -direct 直接从硬盘上(ala5:而非mpq文件中)读取数据
    -lem -lowend 不清楚
    -nocompress -nocompress 无压缩
    -comint -comint 动态数据结构(别碰它)
    -token -token 设置关闭游戏的令牌为
    -gamepass -gamepass 设置游戏密码为
    -skiptobnet -skiptobnet 直接进入battle.net
    10.定制选项
    -client -client 客户端模式
    -server -server 服务器端模式，需要d2server.dll
    -launch -launch 运行模式(默认)
    -notitle -notitle 无窗口标题栏
    -res800 -res800 窗口大小为800x600(仅适用于D2，对D2X无效)
    -res640 -res640 窗口大小为640x480(仅适用于D2，对D2X无效)
    -nonotify -nonotify 关闭错误信息报警
    -noexit -noexit 不自动退出
    -autorest -autorest 退出后自动重新启动游戏
    -multiclient -multiclient 1个cdkey可以启动多个客户端游戏
    -nohook -nohook 禁止Windows钩子
    -nochar -nochar 禁止角色图像
    -clientexit -clientexit 退出游戏时自动关闭客户端游戏程序
    -noscript -noscript 不读取脚本
    -noplugin -noplugin 不导入Plug-in
    -locale -locale 设置语言为：ENG(英语)，CHI(中文)
    -hookwnd -hookwnd 设置钩子窗口类为
    -hookexe -hookexe 设置钩子版本校验game.exe为
    -servername -servername 设置游戏服务器端名字为
    -title -title 设置窗口标题为
    *********************************************************************
    一般情况下:
    -direct -txt
    -mpq file.mpq
    -w
    -locale chi
    这几个就够用了
    第一个是给另类模式调试用的，修改BIN文件比较麻烦，修改TXT文件，然后生成
    BIN文件，如果不需要生成BIN文件，参数还可以增加一个 -rtx
    第二个也可以用于快速加载CDK文件，把2个CDK的MPQ文件作为2个快捷方式，倒
    装备相当方便。
    第三个应该都知道，窗口模式，不多说。
    第四个是把其他语言转化为繁体中文..... 奥美版推荐使用.......需要鸟语版
    的参数改为 -locale eng 即可

    D2loader常用参数使用说明
    为"Diablo II.exe"创建一个快捷方式，就可以加上相应的参数实现不同的启动方式了，多个参数中用空格分开，如果字符串参数中有空格就用双引号引起来，如:
    -title "Diablo II"，就可以使Diablo窗口标题为"Diablo II"，如果不加双引号就成了"Diablo"

    D2loader会自动加载Diablo文件夹下Plugin文件夹里的文件，而用-pdir参数可是指定别的文件夹
    选出常用的参数说明
    红色为最常用参数
    代表数字, 代表字符串
    -w 以窗口模式运行Diablo
    -lq 低图像质量(挂bot常用)
    -ns 无声模式
    -locale 设置语言为 : ENG(英语)，CHI(中文)
    -title 设置窗口标题为

    -skiptobnet 直接进入Battle.net，只是省了点一下鼠标
    -res800 窗口大小为800x600(仅适用于D2，对D2X无效)
    -res640 窗口大小为640x480(仅适用于D2，对D2X无效)
    -notitle 无窗口标题栏
    -nonotify 关闭错误信息报警而直接退出
    -noexit 不自动退出
    -nochar 禁止角色图像
    -pdir 指定插件所在目录
    -mpq file.mpq 加载文件file.mpq，一般用在加载cdkey.mpq，或者加载Mod
    -txt 加载Mod时常用，一般游戏不用

    比较常用的
    -direct -w -lq -title "n" -pdir d2cn -locale eng E文
    -nonotify -title -lq -w -pdir d2cn 中文
    */
}

static BOOL parse_ini(ST_CLIENT_DATA *pstClientData)
{
    const char *acScriptName = "D2Loader.ini";
    FILE *fpListFile = NULL;
    char *pStr = NULL;
    char acBuff[2048] = {0};
    int argc = 0;
    char *argv[100] = {NULL};

    if ( _access(acScriptName, 0) != 0 )
    {
        return FALSE;
    }

    fpListFile = fopen(acScriptName, "r");
    if ( !fpListFile )
    {
        return FALSE;
    }

    if ( NULL == fgets(acBuff, sizeof(acBuff), fpListFile) )
    {
        return FALSE;
    }
    fclose(fpListFile);

    argv[argc++] = (char *)"D2Loader.exe";

    pStr = acBuff;
    while ( 0 != *pStr )
    {
        if ( ' ' == *pStr || '\t' == *pStr )
        {
            pStr++;
            continue;
        }

        if ( '"' == *pStr )
        {
            pStr++;
            argv[argc++] = pStr;

            while ( '"' != *pStr && 0 != *pStr )
            {
                pStr++;
            }
            *pStr = 0;
            pStr++;
        }
        else
        {
            argv[argc++] = pStr;

            while ( ' ' != *pStr && '\t' != *pStr && 0 != *pStr )
            {
                pStr++;
            }
            *pStr = 0;
            pStr++;
        }
    }

    parse_command(argc, argv, pstClientData);

    return TRUE;
}

static BOOL D2Loader_ApplyPatch(void* hGame, const DWORD dwBaseAddress, const DLLPatchStrc* pstPatch)
{
    while ( PATCH_FINISH != pstPatch->dwAddress )
    {
        int iReturn = 0;
        DWORD dwAddress = pstPatch->dwAddress;
        if  ( !dwAddress )
        {
            return FALSE;
        }

        dwAddress += dwBaseAddress;

        DWORD dwData = pstPatch->dwData;
        if ( pstPatch->boolRelative )
        {
            dwData = dwData - (dwAddress + sizeof(dwData));
        }

        void* hAddress = (void*)dwAddress;
        DWORD dwOldPage;

        if ( pstPatch->iPatchSize > 0 )
        {
            BYTE abBuffer[1024];

            for ( size_t i = 0; i < pstPatch->iPatchSize; i++ )
            {
                abBuffer[i] = (BYTE)dwData;
            }

            VirtualProtect(hAddress, pstPatch->iPatchSize, PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &abBuffer, pstPatch->iPatchSize, 0);
            VirtualProtect(hAddress, pstPatch->iPatchSize, dwOldPage, 0);
        }
        else
        {
            VirtualProtect(hAddress, sizeof(dwData), PAGE_EXECUTE_READWRITE, &dwOldPage);
            iReturn = WriteProcessMemory(hGame, hAddress, &dwData, sizeof(dwData), 0);
            VirtualProtect(hAddress, sizeof(dwData), dwOldPage, 0);
        }

        if ( 0 == iReturn )
        {
            return FALSE;
        }

        pstPatch++;
    }

    return TRUE;
}

static DWORD WINAPI D2Loader_GetMpqPath(HMODULE hModule, LPTSTR lpFilename, DWORD nSize)
{
    char acTempPath2[D2LOADER_MAXPATH] = {0};
    DWORD i;

    if ( 0 != m_acModPath[0] )
    {
        memset(acTempPath2, 0, sizeof(acTempPath2));
        strcpy_s(acTempPath2, sizeof(acTempPath2), m_acModPath);
        while ( '\\' == acTempPath2[strlen(acTempPath2) - 1] )
        {
            acTempPath2[strlen(acTempPath2) - 1] = 0;
        }
        sprintf_s(&acTempPath2[strlen(acTempPath2)], sizeof(acTempPath2) - strlen(acTempPath2), "\\%s", lpFilename);

        if ( _access(acTempPath2, 0) == 0 )
        {
            strcpy_s(lpFilename, nSize, acTempPath2);
            return strlen(lpFilename);
        }
    }

    for ( i = 0; i < m_dwGlobalMpqPath; ++i )
    {
        memset(acTempPath2, 0, sizeof(acTempPath2));
        strcpy_s(acTempPath2, sizeof(acTempPath2), m_aacGlobalMpqPath[i]);
        while ( '\\' == acTempPath2[strlen(acTempPath2) - 1] )
        {
            acTempPath2[strlen(acTempPath2) - 1] = 0;
        }
        sprintf_s(&acTempPath2[strlen(acTempPath2)], sizeof(acTempPath2) - strlen(acTempPath2), "\\%s", lpFilename);

        if ( _access(acTempPath2, 0) == 0 )
        {
            strcpy_s(lpFilename, nSize, acTempPath2);
            return strlen(lpFilename);
        }
    }

    memset(acTempPath2, 0, sizeof(acTempPath2));
    strcpy_s(acTempPath2, sizeof(acTempPath2), m_acRunningPath);
    sprintf_s(&acTempPath2[strlen(acTempPath2)], sizeof(acTempPath2) - strlen(acTempPath2), "\\%s", lpFilename);
    if ( _access(acTempPath2, 0) == 0 )
    {
        strcpy_s(lpFilename, nSize, acTempPath2);
        return strlen(lpFilename);
    }

    return strlen(lpFilename);
}

static DWORD WINAPI D2Loader_GetMpqPathExp(LPCTSTR lpFileName)
{
    DWORD i;
    char acTempPath[D2LOADER_MAXPATH] = {0};

    if ( !CP_stristr(lpFileName, "d2exp.mpq") )
    {
        return GetFileAttributes(lpFileName);
    }

    if ( 0 != m_acModPath[0] )
    {
        memset(acTempPath, 0, sizeof(acTempPath));
        strcpy_s(acTempPath, sizeof(acTempPath), m_acModPath);
        while ( '\\' == acTempPath[strlen(acTempPath) - 1] )
        {
            acTempPath[strlen(acTempPath) - 1] = 0;
        }
        sprintf_s(&acTempPath[strlen(acTempPath)], sizeof(acTempPath) - strlen(acTempPath), "\\%s", "d2exp.mpq");
        if ( _access(acTempPath, 0) == 0 )
        {
            return GetFileAttributes(acTempPath);
        }
    }

    for ( i = 0; i < m_dwGlobalMpqPath; ++i )
    {
        memset(acTempPath, 0, sizeof(acTempPath));
        strcpy_s(acTempPath, sizeof(acTempPath), m_aacGlobalMpqPath[i]);
        while ( '\\' == acTempPath[strlen(acTempPath) - 1] )
        {
            acTempPath[strlen(acTempPath) - 1] = 0;
        }
        sprintf_s(&acTempPath[strlen(acTempPath)], sizeof(acTempPath) - strlen(acTempPath), "\\%s", "d2exp.mpq");
        if ( _access(acTempPath, 0) == 0 )
        {
            return GetFileAttributes(acTempPath);
        }
    }

    return GetFileAttributes(lpFileName);
}

static DWORD WINAPI D2Loader_GetMpqPathChar(LPCTSTR lpFileName)
{
    DWORD i;
    char acTempPath[D2LOADER_MAXPATH] = {0};

    if ( !CP_stristr(lpFileName, "d2char.mpq") )
    {
        return GetFileAttributes(lpFileName);
    }

    if ( 0 != m_acModPath[0] )
    {
        memset(acTempPath, 0, sizeof(acTempPath));
        strcpy_s(acTempPath, sizeof(acTempPath), m_acModPath);
        while ( '\\' == acTempPath[strlen(acTempPath) - 1] )
        {
            acTempPath[strlen(acTempPath) - 1] = 0;
        }
        sprintf_s(&acTempPath[strlen(acTempPath)], sizeof(acTempPath) - strlen(acTempPath), "\\%s", "d2char.mpq");
        if ( _access(acTempPath, 0) == 0 )
        {
            return GetFileAttributes(acTempPath);
        }
    }

    for ( i = 0; i < m_dwGlobalMpqPath; ++i )
    {
        memset(acTempPath, 0, sizeof(acTempPath));
        strcpy_s(acTempPath, sizeof(acTempPath), m_aacGlobalMpqPath[i]);
        while ( '\\' == acTempPath[strlen(acTempPath) - 1] )
        {
            acTempPath[strlen(acTempPath) - 1] = 0;
        }
        sprintf_s(&acTempPath[strlen(acTempPath)], sizeof(acTempPath) - strlen(acTempPath), "\\%s", "d2char.mpq");
        if ( _access(acTempPath, 0) == 0 )
        {
            return GetFileAttributes(acTempPath);
        }
    }

    return GetFileAttributes(lpFileName);
}

static DWORD __stdcall D2Loader_GetMpqPath_New(char* lpFileName, DWORD dwPriority, DWORD dwFlags, HANDLE* hMPQ)
{
    char acFileName[D2LOADER_MAXPATH] = {0};

    if ( !lpFileName || 0 == lpFileName[0] || NULL != strstr(lpFileName, ":\\") )
    {
        //空指针或者指定了绝对加载路径的直接返回
        if ( lpFileName && 0 != lpFileName[0] )
        {
            printf("open mpq: %s\r\n", lpFileName);
        }
        return NULL;
    }

    memset(acFileName, 0, sizeof(acFileName));
    strcpy_s(acFileName, sizeof(acFileName), lpFileName);
    if ( 0 < D2Loader_GetMpqPath(NULL, acFileName, sizeof(acFileName)) )
    {
        printf("open mpq: %s\r\n", acFileName);
        strcpy(lpFileName, acFileName);
        return NULL;
    }

    printf("open mpq: %s\r\n", lpFileName);
    return NULL;
}

static DWORD m_dwTemp = 0;
static DWORD m_dwEax = 0;

static __declspec(naked) void D2Loader_GetMpqPath_109()
{
    __asm
    {
        mov m_dwEax,eax
        pop eax
        mov m_dwTemp,eax
        mov eax,m_dwEax
        pushad
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        call D2Loader_GetMpqPath_New
        test eax,eax
        jz orig_code
        mov dword ptr ss:[esp+0x24], eax
orig_code:
        popad
        mov ecx,dword ptr ss:[esp+0x10]
        sub esp,0x224
        push m_dwTemp
        ret
    }
}

static __declspec(naked) void D2Loader_GetMpqPath_111()
{
    __asm
    {
        mov m_dwEax,eax
        pop eax
        mov m_dwTemp,eax
        mov eax,m_dwEax
        pushad
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        call D2Loader_GetMpqPath_New
        test eax,eax
        jz orig_code
        mov dword ptr ss:[esp+0x24], eax
orig_code:
        popad
        mov ecx,dword ptr ss:[esp+0x10]
        sub esp,0x120
        push m_dwTemp
        ret
    }
}

static void D2Loader_PatchMpqPath()
{
    //直接从storm.dll入手了
    DLLPatchStrc astPatches[] =
    {
        //更新获取mpq的文件路径
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0},

        {PATCH_FINISH} // this must be the last entry in the array!
    };
    DWORD dwOffset = (DWORD)m_pfnStormOpenArchive - (DWORD)GetModuleHandle("Storm.dll");

    switch ( m_iGameVersion )
    {
        case V109d:
        case V110f:
            astPatches[0].dwAddress = dwOffset;
            astPatches[0].dwData = PATCH_CALL;
            astPatches[0].boolRelative = FALSE;
            astPatches[0].iPatchSize = 1;
            astPatches[1].dwAddress = dwOffset + 1;
            astPatches[1].dwData = (DWORD)D2Loader_GetMpqPath_109;
            astPatches[1].boolRelative = TRUE;
            astPatches[1].iPatchSize = 0;
            astPatches[2].dwAddress = dwOffset + 5;
            astPatches[2].dwData = PATCH_NOPBLOCK;
            astPatches[2].boolRelative = FALSE;
            astPatches[2].iPatchSize = 5;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
            break;

        case V111b:
        case V112a:
        case V113c:
        case V113d:
            astPatches[0].dwAddress = dwOffset;
            astPatches[0].dwData = PATCH_CALL;
            astPatches[0].boolRelative = FALSE;
            astPatches[0].iPatchSize = 1;
            astPatches[1].dwAddress = dwOffset + 1;
            astPatches[1].dwData = (DWORD)D2Loader_GetMpqPath_111;
            astPatches[1].boolRelative = TRUE;
            astPatches[1].iPatchSize = 0;
            astPatches[2].dwAddress = dwOffset + 5;
            astPatches[2].dwData = PATCH_NOPBLOCK;
            astPatches[2].boolRelative = FALSE;
            astPatches[2].iPatchSize = 5;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
            break;

        default:
            break;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
#if 0
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x143B7, PATCH_CALL, FALSE, 0x01},  //call
                {0x143B8, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x143BC, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
#endif
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0xC0A0, (DWORD)0xBE, FALSE, 0x01},
                {0xC0A1, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0xC0A5, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0x7B49, (DWORD)0xBE, FALSE, 0x01},
                {0x7B4A, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0x7B4E, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            //D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V110f:
        {
#if 0
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x12455, PATCH_CALL, FALSE, 0x01},  //call
                {0x12456, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x1245A, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
#endif
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0xD770, (DWORD)0xBE, FALSE, 0x01},
                {0xD771, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0xD775, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0x5E19, (DWORD)0xBE, FALSE, 0x01},
                {0x5E1A, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0x5E1E, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            //D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V111b:
        {
#if 0
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7BC6, PATCH_CALL, FALSE, 0x01},  //call
                {0x7BC7, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7BCB, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
#endif
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1F700, (DWORD)0xBE, FALSE, 0x01},
                {0x1F701, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1F705, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0xC1F9, (DWORD)0xBE, FALSE, 0x01},
                {0xC1FA, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0xC1FE, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            //D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V112a:
        {
#if 0
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7DE6, PATCH_CALL, FALSE, 0x01},  //call
                {0x7DE7, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7DEB, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
#endif
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1F9E0, (DWORD)0xBE, FALSE, 0x01},
                {0x1F9E1, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1F9E5, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0x87F9, (DWORD)0xBE, FALSE, 0x01},
                {0x87FA, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0x87FE, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            //D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V113c:
        {
#if 0
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7E06, PATCH_CALL, FALSE, 0x01},  //call
                {0x7E07, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7E0B, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
#endif
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1F9F0, (DWORD)0xBE, FALSE, 0x01},
                {0x1F9F1, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1F9F5, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0xA499, (DWORD)0xBE, FALSE, 0x01},
                {0xA49A, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0xA49E, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            //D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        case V113d:
        {
#if 0
            static const DLLPatchStrc astPatches[] =
            {//更新获取mpq的文件路径
                {0x7DF6, PATCH_CALL, FALSE, 0x01},  //call
                {0x7DF7, (DWORD)D2Loader_GetMpqPath, TRUE, 0x00},
                {0x7DFB, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
#endif
            static const DLLPatchStrc astPatches2[] =
            {//判断是否资料片，d2exp.mpq，否则会判断为非资料片版本
                {0x1FB20, (DWORD)0xBE, FALSE, 0x01},
                {0x1FB21, (DWORD)D2Loader_GetMpqPathExp, FALSE, 0x00},
                {0x1FB25, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {//判断是否资料片，d2char.mpq，否则会无法开启TCP/IP多人游戏，只能单机
                {0xCCB9, (DWORD)0xBE, FALSE, 0x01},
                {0xCCBA, (DWORD)D2Loader_GetMpqPathChar, FALSE, 0x00},
                {0xCCBE, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            //D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Fog.dll"), astPatches2);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches3);
        }
            break;

        default:
            break;
    }
}

static BOOL __stdcall D2Loader_SRegLoadString(char *keyname, char *valuename, int a3, char *buffer, size_t buffersize)
{
    if ( !_stricmp("Diablo II", keyname) && !_stricmp("InstallPath", valuename) )
    {
        if ( NULL != CP_stristr(buffer, "PlugY") )
        {
            //PlugY需要特殊处理一下，方便共用资源文件
            sprintf_s(buffer, buffersize, "%s\\PlugY", m_acModPath);
            if ( _access(buffer, 0) == 0 )
            {
                strcpy_s(buffer, buffersize, m_acModPath);
                return TRUE;
            }

            sprintf_s(buffer, buffersize, "%s\\Tools\\PlugY", m_acRunningPath);
        }
        else
        {
            strcpy_s(buffer, buffersize, m_acModPath);
        }
        return TRUE;
    }

    if ( !_stricmp("Diablo II", keyname) && (!_stricmp("Save Path", valuename) || !_stricmp("NewSavePath", valuename)) )
    {
        sprintf_s(buffer, buffersize, "%s\\Save", m_acModPath);
        if ( _access(buffer, 0) == -1 )
        {
            _mkdir(buffer);
        }
        return TRUE;
    }

    return FALSE;
}

static BOOL __stdcall D2Loader_RegSaveString(char *keyname, char *valuename, BYTE flags, char *string)
{
    if ( !_stricmp("Diablo II", keyname) &&
        (!_stricmp("InstallPath", valuename) || !_stricmp("Save Path", valuename) || !_stricmp("NewSavePath", valuename)) )
    {
        //不写注册表
        return TRUE;
    }

    return FALSE;
}

static DWORD m_dwRegSaveStringRet = 0;
static __declspec(naked) void D2Loader_RegSaveString_asm()
{
    __asm
    {
        pushad
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        mov eax,dword ptr ss:[esp+0x30]
        push eax
        call D2Loader_RegSaveString
        test eax,eax
        popad
        jz orig_code
        mov eax, 0x1
        retn 0x10
orig_code:
        push ebx
        push esi
        mov esi,dword ptr ss:[esp+0xC]
        jmp m_dwRegSaveStringRet
    }
}

static void D2Loader_RemoveRegistryUpdate()
{
    //直接从storm.dll入手了
    DLLPatchStrc astPatches[] =
    {
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0},

        {PATCH_FINISH} // this must be the last entry in the array!
    };
    DWORD dwOffset = (DWORD)m_pfnStormRegSaveString - (DWORD)GetModuleHandle("Storm.dll");

    switch ( m_iGameVersion )
    {
        case V109d:
        case V110f:
        case V111b:
        case V112a:
        case V113c:
        case V113d:
            astPatches[0].dwAddress = dwOffset;
            astPatches[0].dwData = PATCH_JMP;
            astPatches[0].boolRelative = FALSE;
            astPatches[0].iPatchSize = 1;
            astPatches[1].dwAddress = dwOffset + 1;
            astPatches[1].dwData = (DWORD)D2Loader_RegSaveString_asm;
            astPatches[1].boolRelative = TRUE;
            astPatches[1].iPatchSize = 0;
            astPatches[2].dwAddress = dwOffset + 5;
            astPatches[2].dwData = PATCH_NOPBLOCK;
            astPatches[2].boolRelative = FALSE;
            astPatches[2].iPatchSize = 1;
            m_dwRegSaveStringRet = (DWORD)GetModuleHandle("Storm.dll") + dwOffset + 6;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
            break;

        default:
            break;
    }
}

DWORD m_dwRegLoadRetAddr = 0;
static __declspec(naked) void D2Loader_SRegLoadString_asm()
{
    __asm
    {
        pushad
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        call D2Loader_SRegLoadString
        test eax,eax
        popad
        jz orig_code
        mov eax, 0x1
        retn 0x14
orig_code:
        mov ecx,dword ptr ss:[esp+0x4]
        mov dl,byte ptr ds:[ecx]
        jmp m_dwRegLoadRetAddr
    }
}

static __declspec(naked) void D2Loader_SRegLoadString_asm_109()
{
    __asm
    {
        pushad
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        mov eax,dword ptr ss:[esp+0x34]
        push eax
        call D2Loader_SRegLoadString
        test eax,eax
        popad
        jz orig_code
        mov eax, 0x1
        retn 0x14
orig_code:
        mov ecx,dword ptr ss:[esp+0x4]
        push ebx
        jmp m_dwRegLoadRetAddr
    }
}

static void D2Loader_PatchModPath()
{
    if ( 0 == m_acModPath[0] )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x268F0, PATCH_JMP, FALSE, 0x01},
                {0x268F1, (DWORD)D2Loader_SRegLoadString_asm_109, TRUE, 0x00},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            m_dwRegLoadRetAddr = (DWORD)GetModuleHandle("Storm.dll") + 0x268F5;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
        }
            break;

        case V110f:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x25A00, PATCH_JMP, FALSE, 0x01},
                {0x25A01, (DWORD)D2Loader_SRegLoadString_asm_109, TRUE, 0x00},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            m_dwRegLoadRetAddr = (DWORD)GetModuleHandle("Storm.dll") + 0x25A05;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
        }
            break;

        case V111b:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x3C230, PATCH_JMP, FALSE, 0x01},
                {0x3C231, (DWORD)D2Loader_SRegLoadString_asm, TRUE, 0x00},
                {0x3C235, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            m_dwRegLoadRetAddr = (DWORD)GetModuleHandle("Storm.dll") + 0x3C236;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
        }
            break;

        case V112a:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x1B750, PATCH_JMP, FALSE, 0x01},
                {0x1B751, (DWORD)D2Loader_SRegLoadString_asm, TRUE, 0x00},
                {0x1B755, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            m_dwRegLoadRetAddr = (DWORD)GetModuleHandle("Storm.dll") + 0x1B756;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
        }
            break;

        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x3C2B0, PATCH_JMP, FALSE, 0x01},
                {0x3C2B1, (DWORD)D2Loader_SRegLoadString_asm, TRUE, 0x00},
                {0x3C2B5, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            m_dwRegLoadRetAddr = (DWORD)GetModuleHandle("Storm.dll") + 0x3C2B6;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
        }
            break;

        case V113d:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x3C220, PATCH_JMP, FALSE, 0x01},
                {0x3C221, (DWORD)D2Loader_SRegLoadString_asm, TRUE, 0x00},
                {0x3C225, PATCH_NOPBLOCK, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            m_dwRegLoadRetAddr = (DWORD)GetModuleHandle("Storm.dll") + 0x3C226;
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Storm.dll"), astPatches);
        }
            break;

        default:
            break;
    }

    D2Loader_RemoveRegistryUpdate();
}

static void D2Loader_PatchDepFix()
{
    if ( TRUE != m_boolDepFix )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0x44D0, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0x44D1, 0x01, FALSE, 0x01},
                {0x44D2, 0x00, FALSE, 0x01},
                {0x44D3, 0x00, FALSE, 0x01},
                {0x44D4, 0x00, FALSE, 0x01},
                {0x44D5, 0xC2, FALSE, 0x01},
                {0x44D6, 0x0C, FALSE, 0x01},
                {0x44D7, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V110f:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0x4480, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0x4481, 0x01, FALSE, 0x01},
                {0x4482, 0x00, FALSE, 0x01},
                {0x4483, 0x00, FALSE, 0x01},
                {0x4484, 0x00, FALSE, 0x01},
                {0x4485, 0xC2, FALSE, 0x01},
                {0x4486, 0x0C, FALSE, 0x01},
                {0x4487, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V111b:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0xCBA0, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0xCBA1, 0x01, FALSE, 0x01},
                {0xCBA2, 0x00, FALSE, 0x01},
                {0xCBA3, 0x00, FALSE, 0x01},
                {0xCBA4, 0x00, FALSE, 0x01},
                {0xCBA5, 0xC2, FALSE, 0x01},
                {0xCBA6, 0x0C, FALSE, 0x01},
                {0xCBA7, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V112a:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0xC200, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0xC201, 0x01, FALSE, 0x01},
                {0xC202, 0x00, FALSE, 0x01},
                {0xC203, 0x00, FALSE, 0x01},
                {0xC204, 0x00, FALSE, 0x01},
                {0xC205, 0xC2, FALSE, 0x01},
                {0xC206, 0x0C, FALSE, 0x01},
                {0xC207, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0xD320, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0xD321, 0x01, FALSE, 0x01},
                {0xD322, 0x00, FALSE, 0x01},
                {0xD323, 0x00, FALSE, 0x01},
                {0xD324, 0x00, FALSE, 0x01},
                {0xD325, 0xC2, FALSE, 0x01},
                {0xD326, 0x0C, FALSE, 0x01},
                {0xD327, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        case V113d:
        {
            static const DLLPatchStrc astPatches[] =
            {//跳过IX86ver1.dll的版本检查
                {0x11090, 0xB8, FALSE, 0x01},  //mov eax,0x1;retn 0xC
                {0x11091, 0x01, FALSE, 0x01},
                {0x11092, 0x00, FALSE, 0x01},
                {0x11093, 0x00, FALSE, 0x01},
                {0x11094, 0x00, FALSE, 0x01},
                {0x11095, 0xC2, FALSE, 0x01},
                {0x11096, 0x0C, FALSE, 0x01},
                {0x11097, 0x00, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
        }
            break;

        default:
            break;
    }
}

static void D2Loader_PatchMultiOpen()
{
    if ( TRUE != m_boolMultiOpen )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x447C, 0xEB, FALSE, 0x01},  //je short 6FA8B6F7-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V110f:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x446A, 0xEB, FALSE, 0x01},  //je short 6FA8B6F7-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V111b:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x84AF, 0xEB, FALSE, 0x01},  //je short 6FA88606-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V112a:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x894F, 0xEB, FALSE, 0x01},  //je short 6FA88996-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x85BF, 0xEB, FALSE, 0x01},  //je short 6FA88606-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        case V113d:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0xB6B0, 0xEB, FALSE, 0x01},  //je short 6FA8B6F7-->jmp

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches);
        }
            break;

        default:
            break;
    }
}

static VOID D2Loader_InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
{
    if ( NULL == lpCriticalSection )
    {
        return;
    }

    /*
    #define RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO         0x01000000
    #define RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN          0x02000000
    #define RTL_CRITICAL_SECTION_FLAG_STATIC_INIT           0x04000000
    #define RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE         0x08000000
    #define RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO      0x10000000
    #define RTL_CRITICAL_SECTION_ALL_FLAG_BITS              0xFF000000
    #define RTL_CRITICAL_SECTION_FLAG_RESERVED              (RTL_CRITICAL_SECTION_ALL_FLAG_BITS & (~(RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO | RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN | RTL_CRITICAL_SECTION_FLAG_STATIC_INIT | RTL_CRITICAL_SECTION_FLAG_RESOURCE_TYPE | RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO)))
    */
    InitializeCriticalSectionEx(lpCriticalSection, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
    return;
}

static void D2Loader_PatchXpCompatible()
{
#if 0
    //这种方法太土了，需要根据版本号patch一大堆dll，土土土
#define DEFINE_PATCHXP_PATCH(offset) \
    {(offset), PATCH_CALL, FALSE, 0x01},\
    {(offset) + 1, (DWORD)D2Loader_InitializeCriticalSection, TRUE, 0x00},\
    {(offset) + 5, PATCH_NOPBLOCK, FALSE, 0x01},

    if ( TRUE != m_boolXpCompatible )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        {
        }
            break;

        case V110f:
        {
        }
            break;

        case V111b:
        {
        }
            break;

        case V112a:
        {
        }
            break;

        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {
                DEFINE_PATCHXP_PATCH(0x6259)
                DEFINE_PATCHXP_PATCH(0xFFC6)
                DEFINE_PATCHXP_PATCH(0x173C4)
                DEFINE_PATCHXP_PATCH(0x18625)
                DEFINE_PATCHXP_PATCH(0x18645)
                DEFINE_PATCHXP_PATCH(0x186A5)

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {
                DEFINE_PATCHXP_PATCH(0x1F42)
                DEFINE_PATCHXP_PATCH(0x9083)
                DEFINE_PATCHXP_PATCH(0xDEDE)
                DEFINE_PATCHXP_PATCH(0xDF14)
                DEFINE_PATCHXP_PATCH(0x45044)

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches3[] =
            {
                DEFINE_PATCHXP_PATCH(0x1292)
                DEFINE_PATCHXP_PATCH(0x4C71)
                DEFINE_PATCHXP_PATCH(0xC23F)
                DEFINE_PATCHXP_PATCH(0xD68E)
                DEFINE_PATCHXP_PATCH(0x12FFE)

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches4[] =
            {
                DEFINE_PATCHXP_PATCH(0x5E41)
                DEFINE_PATCHXP_PATCH(0x12C9E)
                DEFINE_PATCHXP_PATCH(0x70022)

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches5[] =
            {
                DEFINE_PATCHXP_PATCH(0x1DF4)
                DEFINE_PATCHXP_PATCH(0x7DD7)
                DEFINE_PATCHXP_PATCH(0x2C337)
                DEFINE_PATCHXP_PATCH(0x2C60A)
                DEFINE_PATCHXP_PATCH(0x2D4DA)
                DEFINE_PATCHXP_PATCH(0xF78D5)

                {0x4A114, (DWORD)D2Loader_InitializeCriticalSection, FALSE, 0x00},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches6[] =
            {
                DEFINE_PATCHXP_PATCH(0x27A1)
                DEFINE_PATCHXP_PATCH(0x6E65)

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches7[] =
            {
                DEFINE_PATCHXP_PATCH(0x37C1)
                DEFINE_PATCHXP_PATCH(0x7BFE)
                DEFINE_PATCHXP_PATCH(0x7C34)

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches8[] =
            {
                DEFINE_PATCHXP_PATCH(0x4EB7)

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("Bnclient.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Client.dll"), astPatches2);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Cmp.dll"), astPatches3);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Common.dll"), astPatches4);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Game.dll"), astPatches5);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Gdi.dll"), astPatches6);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2gfx.dll"), astPatches7);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Lang.dll"), astPatches8);
        }
            break;

        case V113d:
        {
        }
            break;

        default:
            break;
    }
#else
    //下面是以fog.dll分析出来的ntdll.dll为例，实际上就是InitializeCriticalSection函数失败了
    BYTE* hAddress;
    DWORD dwOldPage, i;
    BYTE bValue = 1;

    if ( TRUE != m_boolXpCompatible )
    {
        return;
    }

    /*
    名称位于 ntdll, 条目 248
     地址=77B97B00
     区段=.text
     类型=输出
     名称=RtlInitializeCriticalSectionEx

    77B41000    36:0038         add byte ptr ss:[eax],bh

    77B97B00 >  8BFF            mov edi,edi
    77B97B02    55              push ebp
    77B97B03    8BEC            mov ebp,esp
    77B97B05    83E4 F8         and esp,-0x8
    77B97B08    83EC 34         sub esp,0x34
    77B97B0B    A1 6033C677     mov eax,dword ptr ds:[0x77C63360]
    77B97B10    33C4            xor eax,esp
    77B97B12    894424 30       mov dword ptr ss:[esp+0x30],eax
    77B97B16    8B45 10         mov eax,dword ptr ss:[ebp+0x10]
    77B97B19    56              push esi
    77B97B1A    8B75 08         mov esi,dword ptr ss:[ebp+0x8]
    77B97B1D    A9 000000E0     test eax,0xE0000000
    77B97B22    75 10           jnz short ntdll.77B97B34
    77B97B24    8BC8            mov ecx,eax
    77B97B26    81E1 00000011   and ecx,0x11000000
    77B97B2C    81F9 00000011   cmp ecx,0x11000000
    77B97B32    75 17           jnz short ntdll.77B97B4B
    77B97B34    B8 F10000C0     mov eax,0xC00000F1
    77B97B39    5E              pop esi
    77B97B3A    8B4C24 30       mov ecx,dword ptr ss:[esp+0x30]
    77B97B3E    33CC            xor ecx,esp
    77B97B40    E8 0BD50100     call ntdll.77BB5050
    77B97B45    8BE5            mov esp,ebp
    77B97B47    5D              pop ebp
    77B97B48    C2 0C00         retn 0xC
    77B97B4B    8B55 0C         mov edx,dword ptr ss:[ebp+0xC]
    77B97B4E    F7C2 000000FF   test edx,0xFF000000
    77B97B54    0F85 D52C0300   jnz ntdll.77BCA82F
    77B97B5A    A9 00000004     test eax,0x4000000
    77B97B5F    0F85 86000000   jnz ntdll.77B97BEB
    77B97B65    64:8B0D 3000000>mov ecx,dword ptr fs:[0x30]
    77B97B6C    C746 04 FFFFFFF>mov dword ptr ds:[esi+0x4],-0x1
    77B97B73    C746 08 0000000>mov dword ptr ds:[esi+0x8],0x0
    77B97B7A    C746 0C 0000000>mov dword ptr ds:[esi+0xC],0x0
    77B97B81    C746 10 0000000>mov dword ptr ds:[esi+0x10],0x0
    77B97B88    8379 64 01      cmp dword ptr ds:[ecx+0x64],0x1
    77B97B8C    0F86 A72C0300   jbe ntdll.77BCA839
    77B97B92    A9 00000002     test eax,0x2000000
    77B97B97    75 66           jnz short ntdll.77B97BFF
    77B97B99    85D2            test edx,edx
    77B97B9B    74 62           je short ntdll.77B97BFF
    77B97B9D    81E2 FFFFFF00   and edx,0xFFFFFF
    77B97BA3    8BC8            mov ecx,eax
    77B97BA5    81E1 00000009   and ecx,0x9000000
    77B97BAB    0BCA            or ecx,edx
    77B97BAD    894E 14         mov dword ptr ds:[esi+0x14],ecx
    77B97BB0    A9 00000010     test eax,0x10000000
    77B97BB5    75 4F           jnz short ntdll.77B97C06
    77B97BB7    803D 00CAC577 0>cmp byte ptr ds:[0x77C5CA00],0x0
    77B97BBE    75 46           jnz short ntdll.77B97C06
    77B97BC0    32C0            xor al,al
    77B97BC2    C706 FFFFFFFF   mov dword ptr ds:[esi],-0x1
    77B97BC8    84C0            test al,al
    77B97BCA    75 3E           jnz short ntdll.77B97C0A
    */

    //hAddress = (void *)(0x77C5CA00 - 0x77B41000/*ntdll.dll*/ + (DWORD)GetModuleHandle("ntdll.dll"));
    hAddress = (BYTE *)GetDllOffset2("ntdll.dll", "RtlInitializeCriticalSectionEx");
    for ( i = 0; i < 512; ++i )
    {
        //搜索关键指令，自动找到0x77C5CA00
        if ( 0x80 == *(hAddress + i) && 0x3D == *(hAddress + i + 1) && 0x75 == *(hAddress + i + 7) && 0x75 == *(hAddress + i - 2) )
        {
            break;
        }
    }
    if ( 512 <= i )
    {
        return;
    }
    hAddress = (BYTE *)*(DWORD *)(hAddress + i + 2);    //取出0x77C5CA00
    VirtualProtect(hAddress, 1, PAGE_EXECUTE_READWRITE, &dwOldPage);
    WriteProcessMemory(GetCurrentProcess(), hAddress, &bValue, 1, 0);
    VirtualProtect(hAddress, 1, dwOldPage, 0);
#endif
}

static void *m_pvClientData = NULL;

static void *D2Loader_InitClientData(ST_CLIENT_DATA *pstClientData)
{
    if ( NULL != m_pvClientData )
    {
        return m_pvClientData;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_109));
            break;

        case V110f:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_110));
            break;

        case V111b:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_111));
            break;

        case V112a:
            m_pvClientData = malloc(sizeof(ST_CLIENT_DATA_112));
            break;

        case V113c:
        case V113d:
            m_pvClientData = pstClientData;
            break;

        default:
            break;
    }

    return m_pvClientData;
}

static void D2Loader_FreeClientData(ST_CLIENT_DATA *pstClientData)
{
    if ( NULL != m_pvClientData && m_pvClientData != pstClientData )
    {
        free(m_pvClientData);
    }
    m_pvClientData = NULL;
}

static void D2Loader_PutClientData(ST_CLIENT_DATA *pstClientData)
{
    switch ( m_iGameVersion )
    {
        case V109d:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->teen - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_109 *)m_pvClientData)->no_sound), &pstClientData->no_sound, 3);
            memcpy(&(((ST_CLIENT_DATA_109 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V110f:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_110 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V111b:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_111 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V112a:
            memcpy(m_pvClientData, pstClientData, 5);
            memcpy((void *)((DWORD)m_pvClientData + 5), (void *)((DWORD)pstClientData + 6), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&(((ST_CLIENT_DATA_112 *)m_pvClientData)->bnet_callbacks), &pstClientData->bnet_callbacks, sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V113c:
        case V113d:
            break;

        default:
            break;
    }
}

static void D2Loader_GetClientData(ST_CLIENT_DATA *pstClientData)
{
    switch ( m_iGameVersion )
    {
        case V109d:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->teen - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->no_sound, &(((ST_CLIENT_DATA_109 *)m_pvClientData)->no_sound), 3);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_109 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V110f:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_110 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V111b:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_111 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V112a:
            memcpy(pstClientData, m_pvClientData, 5);
            memcpy((void *)((DWORD)pstClientData + 6), (void *)((DWORD)m_pvClientData + 5), (DWORD)&pstClientData->sound_background - (DWORD)&pstClientData->glide_mode);
            memcpy(&pstClientData->bnet_callbacks, &(((ST_CLIENT_DATA_112 *)m_pvClientData)->bnet_callbacks), sizeof(ST_CLIENT_DATA) - ((DWORD)&pstClientData->bnet_callbacks - (DWORD)pstClientData));
            break;

        case V113c:
        case V113d:
            break;

        default:
            break;
    }
}

static void D2Loader_CheckStruct()
{
#define CHECK_STRUCT(strname, varname, offset)  if ( offset != (DWORD)&(strname->varname) - (DWORD)strname ) {printf("fail "#varname"[%d] for "#strname"[%d]\r\n", (DWORD)&(strname->varname) - (DWORD)strname, offset);return;}
    ST_CLIENT_DATA *pstClientData = (ST_CLIENT_DATA *)malloc(sizeof(ST_CLIENT_DATA));
    ST_CLIENT_DATA_109 *pstClientData109 = (ST_CLIENT_DATA_109 *)malloc(sizeof(ST_CLIENT_DATA_109));
    ST_CLIENT_DATA_110 *pstClientData110 = (ST_CLIENT_DATA_110 *)malloc(sizeof(ST_CLIENT_DATA_110));
    ST_CLIENT_DATA_111 *pstClientData111 = (ST_CLIENT_DATA_111 *)malloc(sizeof(ST_CLIENT_DATA_111));
    ST_CLIENT_DATA_112 *pstClientData112 = (ST_CLIENT_DATA_112 *)malloc(sizeof(ST_CLIENT_DATA_112));

    //1.09d
    printf("start checking 1.09d\r\n");
    CHECK_STRUCT(pstClientData109, window_mode, 0x04)
    CHECK_STRUCT(pstClientData109, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData109, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData109, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData109, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData109, perspective, 0x09)
    CHECK_STRUCT(pstClientData109, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData109, gamma, 0x0B)
    CHECK_STRUCT(pstClientData109, vsync, 0x0F)
    CHECK_STRUCT(pstClientData109, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData109, join_id, 0x18)
    CHECK_STRUCT(pstClientData109, game_name, 0x1A)
    CHECK_STRUCT(pstClientData109, game_ip, 0x32)
    CHECK_STRUCT(pstClientData109, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData109, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData109, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData109, open_c, 0x7F)
    CHECK_STRUCT(pstClientData109, amazon, 0x80)
    CHECK_STRUCT(pstClientData109, paladin, 0x81)
    CHECK_STRUCT(pstClientData109, sorceress, 0x82)
    CHECK_STRUCT(pstClientData109, necromancer, 0x83)
    CHECK_STRUCT(pstClientData109, barbarian, 0x84)
    CHECK_STRUCT(pstClientData109, invincible, 0x87)
    CHECK_STRUCT(pstClientData109, player_name, 0xB8)
    CHECK_STRUCT(pstClientData109, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData109, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData109, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData109, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData109, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData109, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData109, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData109, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData109, act, 0x01FA)
    CHECK_STRUCT(pstClientData109, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData109, direct, 0x01FF)
    CHECK_STRUCT(pstClientData109, low_end, 0x0200)
    CHECK_STRUCT(pstClientData109, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData109, arena, 0x0202)
    CHECK_STRUCT(pstClientData109, txt, 0x0210)
    CHECK_STRUCT(pstClientData109, log, 0x0211)
    CHECK_STRUCT(pstClientData109, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData109, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData109, no_save, 0x0214)
    CHECK_STRUCT(pstClientData109, seed, 0x0215)
    CHECK_STRUCT(pstClientData109, cheats, 0x0219)
    CHECK_STRUCT(pstClientData109, no_sound, 0x021A)
    CHECK_STRUCT(pstClientData109, quests, 0x021B)
    CHECK_STRUCT(pstClientData109, skip_to_bnet, 0x0355)
    printf("finish checking 1.09d\r\n");

    //1.10f
    printf("start checking 1.10f\r\n");
    CHECK_STRUCT(pstClientData110, window_mode, 0x04)
    CHECK_STRUCT(pstClientData110, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData110, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData110, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData110, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData110, perspective, 0x09)
    CHECK_STRUCT(pstClientData110, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData110, gamma, 0x0B)
    CHECK_STRUCT(pstClientData110, vsync, 0x0F)
    CHECK_STRUCT(pstClientData110, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData110, join_id, 0x18)
    CHECK_STRUCT(pstClientData110, game_name, 0x1A)
    CHECK_STRUCT(pstClientData110, game_ip, 0x32)
    CHECK_STRUCT(pstClientData110, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData110, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData110, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData110, open_c, 0x7F)
    CHECK_STRUCT(pstClientData110, amazon, 0x80)
    CHECK_STRUCT(pstClientData110, paladin, 0x81)
    CHECK_STRUCT(pstClientData110, sorceress, 0x82)
    CHECK_STRUCT(pstClientData110, necromancer, 0x83)
    CHECK_STRUCT(pstClientData110, barbarian, 0x84)
    CHECK_STRUCT(pstClientData110, invincible, 0x87)
    CHECK_STRUCT(pstClientData110, player_name, 0xB8)
    CHECK_STRUCT(pstClientData110, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData110, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData110, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData110, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData110, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData110, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData110, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData110, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData110, act, 0x01FA)
    CHECK_STRUCT(pstClientData110, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData110, direct, 0x01FF)
    CHECK_STRUCT(pstClientData110, low_end, 0x0200)
    CHECK_STRUCT(pstClientData110, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData110, arena, 0x0202)
    CHECK_STRUCT(pstClientData110, txt, 0x0210)
    CHECK_STRUCT(pstClientData110, log, 0x0211)
    CHECK_STRUCT(pstClientData110, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData110, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData110, no_save, 0x0214)
    CHECK_STRUCT(pstClientData110, seed, 0x0215)
    CHECK_STRUCT(pstClientData110, cheats, 0x0219)
    CHECK_STRUCT(pstClientData110, teen, 0x021A)
    CHECK_STRUCT(pstClientData110, no_sound, 0x021B)
    CHECK_STRUCT(pstClientData110, quests, 0x021C)
    CHECK_STRUCT(pstClientData110, build, 0x021E)
    CHECK_STRUCT(pstClientData110, skip_to_bnet, 0x0357)
    printf("finish checking 1.10f\r\n");

    //1.11b
    printf("start checking 1.11b\r\n");
    CHECK_STRUCT(pstClientData111, window_mode, 0x04)
    CHECK_STRUCT(pstClientData111, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData111, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData111, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData111, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData111, perspective, 0x09)
    CHECK_STRUCT(pstClientData111, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData111, gamma, 0x0B)
    CHECK_STRUCT(pstClientData111, vsync, 0x0F)
    CHECK_STRUCT(pstClientData111, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData111, join_id, 0x18)
    CHECK_STRUCT(pstClientData111, game_name, 0x1A)
    CHECK_STRUCT(pstClientData111, game_ip, 0x32)
    CHECK_STRUCT(pstClientData111, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData111, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData111, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData111, open_c, 0x7F)
    CHECK_STRUCT(pstClientData111, amazon, 0x80)
    CHECK_STRUCT(pstClientData111, paladin, 0x81)
    CHECK_STRUCT(pstClientData111, sorceress, 0x82)
    CHECK_STRUCT(pstClientData111, necromancer, 0x83)
    CHECK_STRUCT(pstClientData111, barbarian, 0x84)
    CHECK_STRUCT(pstClientData111, invincible, 0x87)
    CHECK_STRUCT(pstClientData111, player_name, 0xB8)
    CHECK_STRUCT(pstClientData111, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData111, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData111, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData111, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData111, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData111, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData111, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData111, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData111, act, 0x01FA)
    CHECK_STRUCT(pstClientData111, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData111, direct, 0x01FF)
    CHECK_STRUCT(pstClientData111, low_end, 0x0200)
    CHECK_STRUCT(pstClientData111, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData111, arena, 0x0202)
    CHECK_STRUCT(pstClientData111, txt, 0x0210)
    CHECK_STRUCT(pstClientData111, log, 0x0211)
    CHECK_STRUCT(pstClientData111, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData111, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData111, no_save, 0x0214)
    CHECK_STRUCT(pstClientData111, seed, 0x0215)
    CHECK_STRUCT(pstClientData111, cheats, 0x0219)
    CHECK_STRUCT(pstClientData111, teen, 0x021A)
    CHECK_STRUCT(pstClientData111, no_sound, 0x021B)
    CHECK_STRUCT(pstClientData111, quests, 0x021C)
    CHECK_STRUCT(pstClientData111, build, 0x021E)
    CHECK_STRUCT(pstClientData111, skip_to_bnet, 0x0357)
    printf("finish checking 1.11b\r\n");

    //1.12a
    printf("start checking 1.12a\r\n");
    CHECK_STRUCT(pstClientData112, window_mode, 0x04)
    CHECK_STRUCT(pstClientData112, glide_mode, 0x05)
    CHECK_STRUCT(pstClientData112, opengl_mode, 0x06)
    CHECK_STRUCT(pstClientData112, rave_mode, 0x07)
    CHECK_STRUCT(pstClientData112, d3d_mode, 0x08)
    CHECK_STRUCT(pstClientData112, perspective, 0x09)
    CHECK_STRUCT(pstClientData112, low_quality, 0x0A)
    CHECK_STRUCT(pstClientData112, gamma, 0x0B)
    CHECK_STRUCT(pstClientData112, vsync, 0x0F)
    CHECK_STRUCT(pstClientData112, frame_rate, 0x10)
    CHECK_STRUCT(pstClientData112, join_id, 0x18)
    CHECK_STRUCT(pstClientData112, game_name, 0x1A)
    CHECK_STRUCT(pstClientData112, game_ip, 0x32)
    CHECK_STRUCT(pstClientData112, bnet_ip, 0x4A)
    CHECK_STRUCT(pstClientData112, mcp_ip, 0x62)
    CHECK_STRUCT(pstClientData112, no_pk, 0x7E)
    CHECK_STRUCT(pstClientData112, open_c, 0x7F)
    CHECK_STRUCT(pstClientData112, amazon, 0x80)
    CHECK_STRUCT(pstClientData112, paladin, 0x81)
    CHECK_STRUCT(pstClientData112, sorceress, 0x82)
    CHECK_STRUCT(pstClientData112, necromancer, 0x83)
    CHECK_STRUCT(pstClientData112, barbarian, 0x84)
    CHECK_STRUCT(pstClientData112, invincible, 0x87)
    CHECK_STRUCT(pstClientData112, player_name, 0xB8)
    CHECK_STRUCT(pstClientData112, realm_name, 0xD0)
    CHECK_STRUCT(pstClientData112, c_temp, 0x01E8)
    CHECK_STRUCT(pstClientData112, no_monsters, 0x01EC)
    CHECK_STRUCT(pstClientData112, monster_class, 0x01ED)
    CHECK_STRUCT(pstClientData112, monster_info, 0x01F1)
    CHECK_STRUCT(pstClientData112, monster_debug, 0x01F2)
    CHECK_STRUCT(pstClientData112, item_rare, 0x01F6)
    CHECK_STRUCT(pstClientData112, item_unique, 0x01F7)
    CHECK_STRUCT(pstClientData112, act, 0x01FA)
    CHECK_STRUCT(pstClientData112, no_preload, 0x01FE)
    CHECK_STRUCT(pstClientData112, direct, 0x01FF)
    CHECK_STRUCT(pstClientData112, low_end, 0x0200)
    CHECK_STRUCT(pstClientData112, no_gfx_compress, 0x0201)
    CHECK_STRUCT(pstClientData112, arena, 0x0202)
    CHECK_STRUCT(pstClientData112, txt, 0x0210)
    CHECK_STRUCT(pstClientData112, log, 0x0211)
    CHECK_STRUCT(pstClientData112, msg_log, 0x0212)
    CHECK_STRUCT(pstClientData112, safe_mode, 0x0213)
    CHECK_STRUCT(pstClientData112, no_save, 0x0214)
    CHECK_STRUCT(pstClientData112, seed, 0x0215)
    CHECK_STRUCT(pstClientData112, cheats, 0x0219)
    CHECK_STRUCT(pstClientData112, teen, 0x021A)
    CHECK_STRUCT(pstClientData112, no_sound, 0x021B)
    CHECK_STRUCT(pstClientData112, quests, 0x021C)
    CHECK_STRUCT(pstClientData112, build, 0x021E)
    CHECK_STRUCT(pstClientData112, skip_to_bnet, 0x0357)
    printf("finish checking 1.12a\r\n");

    //1.13c
    printf("start checking 1.13c\r\n");
    CHECK_STRUCT(pstClientData, window_mode, 0x04)
    CHECK_STRUCT(pstClientData, fix_aspect_ratio, 0x05)
    CHECK_STRUCT(pstClientData, glide_mode, 0x06)
    CHECK_STRUCT(pstClientData, opengl_mode, 0x07)
    CHECK_STRUCT(pstClientData, rave_mode, 0x08)
    CHECK_STRUCT(pstClientData, d3d_mode, 0x09)
    CHECK_STRUCT(pstClientData, perspective, 0x0A)
    CHECK_STRUCT(pstClientData, low_quality, 0x0B)
    CHECK_STRUCT(pstClientData, gamma, 0x0C)
    CHECK_STRUCT(pstClientData, vsync, 0x10)
    CHECK_STRUCT(pstClientData, frame_rate, 0x11)
    CHECK_STRUCT(pstClientData, join_id, 0x19)
    CHECK_STRUCT(pstClientData, game_name, 0x1B)
    CHECK_STRUCT(pstClientData, game_ip, 0x33)
    CHECK_STRUCT(pstClientData, bnet_ip, 0x4B)
    CHECK_STRUCT(pstClientData, mcp_ip, 0x63)
    CHECK_STRUCT(pstClientData, no_pk, 0x7F)
    CHECK_STRUCT(pstClientData, open_c, 0x80)
    CHECK_STRUCT(pstClientData, amazon, 0x81)
    CHECK_STRUCT(pstClientData, paladin, 0x82)
    CHECK_STRUCT(pstClientData, sorceress, 0x83)
    CHECK_STRUCT(pstClientData, necromancer, 0x84)
    CHECK_STRUCT(pstClientData, barbarian, 0x85)
    CHECK_STRUCT(pstClientData, invincible, 0x88)
    CHECK_STRUCT(pstClientData, player_name, 0xB9)
    CHECK_STRUCT(pstClientData, realm_name, 0xD1)
    CHECK_STRUCT(pstClientData, c_temp, 0x01E9)
    CHECK_STRUCT(pstClientData, no_monsters, 0x01ED)
    CHECK_STRUCT(pstClientData, monster_class, 0x01EE)
    CHECK_STRUCT(pstClientData, monster_info, 0x01F2)
    CHECK_STRUCT(pstClientData, monster_debug, 0x01F3)
    CHECK_STRUCT(pstClientData, item_rare, 0x01F7)
    CHECK_STRUCT(pstClientData, item_unique, 0x01F8)
    CHECK_STRUCT(pstClientData, act, 0x01FB)
    CHECK_STRUCT(pstClientData, no_preload, 0x01FF)
    CHECK_STRUCT(pstClientData, direct, 0x0200)
    CHECK_STRUCT(pstClientData, low_end, 0x0201)
    CHECK_STRUCT(pstClientData, no_gfx_compress, 0x0202)
    CHECK_STRUCT(pstClientData, arena, 0x0203)
    CHECK_STRUCT(pstClientData, txt, 0x0211)
    CHECK_STRUCT(pstClientData, log, 0x0212)
    CHECK_STRUCT(pstClientData, msg_log, 0x0213)
    CHECK_STRUCT(pstClientData, safe_mode, 0x0214)
    CHECK_STRUCT(pstClientData, no_save, 0x0215)
    CHECK_STRUCT(pstClientData, seed, 0x0216)
    CHECK_STRUCT(pstClientData, cheats, 0x021A)
    CHECK_STRUCT(pstClientData, teen, 0x021B)
    CHECK_STRUCT(pstClientData, no_sound, 0x021C)
    CHECK_STRUCT(pstClientData, quests, 0x021D)
    CHECK_STRUCT(pstClientData, build, 0x021F)
    CHECK_STRUCT(pstClientData, sound_background, 0x0220)
    CHECK_STRUCT(pstClientData, skip_to_bnet, 0x0359)
    printf("finish checking 1.13c\r\n");

    //1.13d
    printf("start checking 1.13d\r\n");
    CHECK_STRUCT(pstClientData, window_mode, 0x04)
    CHECK_STRUCT(pstClientData, fix_aspect_ratio, 0x05)
    CHECK_STRUCT(pstClientData, glide_mode, 0x06)
    CHECK_STRUCT(pstClientData, opengl_mode, 0x07)
    CHECK_STRUCT(pstClientData, rave_mode, 0x08)
    CHECK_STRUCT(pstClientData, d3d_mode, 0x09)
    CHECK_STRUCT(pstClientData, perspective, 0x0A)
    CHECK_STRUCT(pstClientData, low_quality, 0x0B)
    CHECK_STRUCT(pstClientData, gamma, 0x0C)
    CHECK_STRUCT(pstClientData, vsync, 0x10)
    CHECK_STRUCT(pstClientData, frame_rate, 0x11)
    CHECK_STRUCT(pstClientData, join_id, 0x19)
    CHECK_STRUCT(pstClientData, game_name, 0x1B)
    CHECK_STRUCT(pstClientData, game_ip, 0x33)
    CHECK_STRUCT(pstClientData, bnet_ip, 0x4B)
    CHECK_STRUCT(pstClientData, mcp_ip, 0x63)
    CHECK_STRUCT(pstClientData, no_pk, 0x7F)
    CHECK_STRUCT(pstClientData, open_c, 0x80)
    CHECK_STRUCT(pstClientData, amazon, 0x81)
    CHECK_STRUCT(pstClientData, paladin, 0x82)
    CHECK_STRUCT(pstClientData, sorceress, 0x83)
    CHECK_STRUCT(pstClientData, necromancer, 0x84)
    CHECK_STRUCT(pstClientData, barbarian, 0x85)
    CHECK_STRUCT(pstClientData, invincible, 0x88)
    CHECK_STRUCT(pstClientData, player_name, 0xB9)
    CHECK_STRUCT(pstClientData, realm_name, 0xD1)
    CHECK_STRUCT(pstClientData, c_temp, 0x01E9)
    CHECK_STRUCT(pstClientData, no_monsters, 0x01ED)
    CHECK_STRUCT(pstClientData, monster_class, 0x01EE)
    CHECK_STRUCT(pstClientData, monster_info, 0x01F2)
    CHECK_STRUCT(pstClientData, monster_debug, 0x01F3)
    CHECK_STRUCT(pstClientData, item_rare, 0x01F7)
    CHECK_STRUCT(pstClientData, item_unique, 0x01F8)
    CHECK_STRUCT(pstClientData, act, 0x01FB)
    CHECK_STRUCT(pstClientData, no_preload, 0x01FF)
    CHECK_STRUCT(pstClientData, direct, 0x0200)
    CHECK_STRUCT(pstClientData, low_end, 0x0201)
    CHECK_STRUCT(pstClientData, no_gfx_compress, 0x0202)
    CHECK_STRUCT(pstClientData, arena, 0x0203)
    CHECK_STRUCT(pstClientData, txt, 0x0211)
    CHECK_STRUCT(pstClientData, log, 0x0212)
    CHECK_STRUCT(pstClientData, msg_log, 0x0213)
    CHECK_STRUCT(pstClientData, safe_mode, 0x0214)
    CHECK_STRUCT(pstClientData, no_save, 0x0215)
    CHECK_STRUCT(pstClientData, seed, 0x0216)
    CHECK_STRUCT(pstClientData, cheats, 0x021A)
    CHECK_STRUCT(pstClientData, teen, 0x021B)
    CHECK_STRUCT(pstClientData, no_sound, 0x021C)
    CHECK_STRUCT(pstClientData, quests, 0x021D)
    CHECK_STRUCT(pstClientData, build, 0x021F)
    CHECK_STRUCT(pstClientData, sound_background, 0x0220)
    CHECK_STRUCT(pstClientData, skip_to_bnet, 0x0359)
    printf("finish checking 1.13d\r\n");

    free(pstClientData);
    free(pstClientData109);
    free(pstClientData110);
    free(pstClientData111);
    free(pstClientData112);
}

static void D2Loader_PatchHackScript(char *acScriptName)
{
    FILE *fpListFile = NULL;
    char *pStr = NULL, *pcTemp = NULL;
    char acBuff[1000] = {0};
    char acDllName[256] = {0};
    volatile DWORD dwOffset = 0xFFFFFFFF, dwType = 0xFFFFFFFF, dwMethod = 0xFFFFFFFF, dwCheck = 0xFFFFFFFF, dwTemp;
    BYTE abValueOld[256] = {0};
    BYTE abValueNew[256] = {0};
    BYTE abTemp[256] = {0};
    char acDesc[D2LOADER_MAXPATH] = {0};
    DWORD dwPatchLen;
    volatile DWORD dwAddress;
    DWORD dwOldPage;
    BOOL boolPatch;

    if ( 0 == acScriptName[0] )
    {
        return;
    }

    fpListFile = fopen(acScriptName, "r");
    if ( !fpListFile )
    {
        return;
    }

    while ( NULL != fgets(acBuff, sizeof(acBuff), fpListFile) )
    {
        if ( '#' == acBuff[0] )
        {
            continue;
        }

        memset(acDesc, 0, sizeof(acDesc));
        pStr = strtok(acBuff, "\r\n\t ");
        while ( pStr )
        {
            if ( '0' == *pStr && ('x' == pStr[1] || 'X' == pStr[1]) )
            {
                pStr += 2;
            }

            if ( 0 == acDllName[0] )
            {
                strncpy(acDllName, pStr, sizeof(acDllName));
            }
            else if ( 0xFFFFFFFF == dwOffset )
            {
                sscanf(pStr, "%x", &dwOffset);
            }
            else if ( 0xFFFFFFFF == dwType )
            {
                sscanf(pStr, "%d", &dwType);
            }
            else if ( 0 == abValueOld[0] )
            {
                dwPatchLen = 0;

                switch ( dwType )
                {
                    case 1:
                        sscanf(pStr, "%x", &dwTemp);
                        abValueOld[3] = (BYTE)((dwTemp & 0xFF000000) >> 24);
                        abValueOld[2] = (BYTE)((dwTemp & 0x00FF0000) >> 16);
                        abValueOld[1] = (BYTE)((dwTemp & 0x0000FF00) >> 8);
                        abValueOld[0] = (BYTE)(dwTemp & 0x000000FF);
                        dwPatchLen = sizeof(DWORD);
                        break;

                    case 2:
                        sscanf(pStr, "%x", &dwTemp);
                        abValueOld[1] = (BYTE)(((WORD)dwTemp & 0xFF00) >> 8);
                        abValueOld[0] = (BYTE)((WORD)dwTemp & 0x00FF);
                        dwPatchLen = sizeof(WORD);
                        break;

                    default:
                        while ( 0 != pStr[dwPatchLen * 2] )
                        {
                            abTemp[0] = pStr[dwPatchLen * 2];
                            abTemp[1] = pStr[dwPatchLen * 2 + 1];
                            abTemp[2] = 0;
                            sscanf((const char *)abTemp, "%x", &dwTemp);
                            abValueOld[dwPatchLen] = (BYTE)dwTemp;
                            dwPatchLen++;
                        }
                        break;
                }
            }
            else if ( 0 == abValueNew[0] )
            {
                dwPatchLen = 0;

                switch ( dwType )
                {
                    case 1:
                        sscanf(pStr, "%x", &dwTemp);
                        abValueNew[3] = (BYTE)((dwTemp & 0xFF000000) >> 24);
                        abValueNew[2] = (BYTE)((dwTemp & 0x00FF0000) >> 16);
                        abValueNew[1] = (BYTE)((dwTemp & 0x0000FF00) >> 8);
                        abValueNew[0] = (BYTE)(dwTemp & 0x000000FF);
                        dwPatchLen = sizeof(DWORD);
                        break;

                    case 2:
                        sscanf(pStr, "%x", &dwTemp);
                        abValueNew[1] = (BYTE)(((WORD)dwTemp & 0xFF00) >> 8);
                        abValueNew[0] = (BYTE)((WORD)dwTemp & 0x00FF);
                        dwPatchLen = sizeof(WORD);
                        break;

                    default:
                        while ( 0 != pStr[dwPatchLen * 2] )
                        {
                            abTemp[0] = pStr[dwPatchLen * 2];
                            abTemp[1] = pStr[dwPatchLen * 2 + 1];
                            abTemp[2] = 0;
                            sscanf((const char *)abTemp, "%x", &dwTemp);
                            abValueNew[dwPatchLen] = (BYTE)dwTemp;
                            dwPatchLen++;
                        }
                        break;
                }
            }
            else if ( 0xFFFFFFFF == dwMethod )
            {
                sscanf(pStr, "%x", &dwMethod);
            }
            else if ( 0xFFFFFFFF == dwCheck )
            {
                dwCheck = atoi(pStr);
            }
            else
            {
                sprintf_s(&acDesc[strlen(acDesc)], sizeof(acDesc) - strlen(acDesc) - 1, "%s ", pStr);
            }

            pStr = strtok(NULL, "\r\n\t ");
        }

        if ( 0 != acDllName[0] )
        {
            if ( NULL == GetModuleHandle(acDllName) )
            {
                D2Loader_LoadLibrary(acDllName);
            }
            if ( NULL != GetModuleHandle(acDllName) )
            {
                boolPatch = TRUE;
                dwAddress = (DWORD)GetModuleHandle(acDllName) + dwOffset;
                VirtualProtect((void *)dwAddress, dwPatchLen, PAGE_EXECUTE_READWRITE, &dwOldPage);
                if ( 0 != dwCheck )
                {
                    ReadProcessMemory(GetCurrentProcess(), (void *)dwAddress, abTemp, dwPatchLen, 0);
                    if ( memcmp(abTemp, abValueOld, dwPatchLen) )
                    {
                        boolPatch = FALSE;
                    }
                }

                if ( TRUE == boolPatch )
                {
                    printf("patch %s[0x%08X:0x%X]-%s\r\n", acDllName, (DWORD)GetModuleHandle(acDllName), dwOffset, acDesc);
                    WriteProcessMemory(GetCurrentProcess(), (void *)dwAddress, abValueNew, dwPatchLen, 0);
                }
                else
                {
                    printf("skip %s[0x%08X:0x%X]-%s\r\n", acDllName, (DWORD)GetModuleHandle(acDllName), dwOffset, acDesc);
                }
                VirtualProtect((void *)dwAddress, dwPatchLen, dwOldPage, 0);
            }
        }

        acDllName[0] = 0;
        dwOffset = 0xFFFFFFFF;
        dwMethod = 0xFFFFFFFF;
        dwCheck = 0xFFFFFFFF;
        dwType = 0xFFFFFFFF;
        abValueOld[0] = {0};
        abValueNew[0] = {0};
    }

    fclose(fpListFile);
}

static LRESULT __stdcall D2Loader_NoHideWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if ( NULL == m_OldLongWinProc )
    {
        return 0;
    }

    if ( WM_ACTIVATEAPP == uMsg && 0 == wParam )
    {
        return 0;
    }

    return CallWindowProc((WNDPROC)m_OldLongWinProc, hwnd, uMsg, wParam, lParam);
}

static void D2Loader_PatchNoSleep()
{
    if ( TRUE != m_boolNoSleep )
    {
        return;
    }

    switch ( m_iGameVersion )
    {
        case V113c:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x43989, (DWORD)0x1, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {
                {0x18A5F, PATCH_NOPBLOCK, FALSE, 0x03},
                {0x18A62, (DWORD)0x6A, FALSE, 0x01},    //push 0x1
                {0x18A63, (DWORD)0x01, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Client.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches2);
        }
            break;

        case V113d:
        {
            static const DLLPatchStrc astPatches[] =
            {
                {0x44929, (DWORD)0x1, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };
            static const DLLPatchStrc astPatches2[] =
            {
                {0xEDAF, PATCH_NOPBLOCK, FALSE, 0x03},
                {0xEDB2, (DWORD)0x6A, FALSE, 0x01},    //push 0x1
                {0xEDB3, (DWORD)0x01, FALSE, 0x01},

                {PATCH_FINISH} // this must be the last entry in the array!
            };

            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Client.dll"), astPatches);
            D2Loader_ApplyPatch(GetCurrentProcess(), (DWORD)GetModuleHandle("D2Win.dll"), astPatches2);
        }

            break;

        default:
            break;
    }
}

static void print_help()
{
#if 1
    printf("版本 %s，请阅读\"功能说明.txt\"", D2LOADER_VERSION_STR);
#else
    printf("作者：Anders 2021.5.7"
"1、一键安装、一键卸载，因为原版的Storm.dll众所周知的校验，所以要使用自定义loader的话，需要先运行D2LoaderInstall.exe对Storm.dll进行patch，不需要的时候可以用D2LoaderRestore.exe进行恢复。如果用我打包的storm.dll，就不需要D2LoaderInstall.exe和D2LoaderRestore.exe了。\r\n"
"2、支持原版game.exe的所有命令行参数，比如-w、-direct、-txt、-glide、-3dfx、-locale、-title等。\r\n"
"3、兼容v1.09d/1.10f/1.11b/1.12a/1.13c/1.13d。\r\n"
"4、默认根据同目录下的game.exe来判断版本号，也可以用参数-ver 1.13c来指定游戏版本号。\r\n"
"5、支持单人、TCP/IP、战网。\r\n"
"6、参数说明：\r\n"
"-ver 指定游戏版本号，比如-ver 1.13d\r\n"
"-xp 自动设置XP兼容，不需要在exe上右键设置了\r\n"
"-console 开启console窗口，看到输出信息\r\n"
"-hack 类似d2gs的动态patch，其中d2hack.script文件名可以任意替换成自己的，具体的文件格式可以参考Install目录下的例子，已经实现了1.13c的一拳满级和一元购物\r\n"
"-hackpre 这个参数指定的script会在其他插件之前加载，而-hack是在其他插件之后加载，适配多种情况\r\n"
"-glide/-3dfx 3dfx模式\r\n"
"-w -direct -txt 不用介绍了\r\n"
"-title 设定游戏窗口标题\r\n"
"-notitle 游戏窗口不显示任何标题\r\n"
"-ns -nosound 无声模式\r\n"
"-res800 -res640 设定游戏分辨率\r\n"
"-locale 比如-locale CHI，则自动加载Language_CHI\\\\CHI.mpq\r\n"
"-mpq 指定额外的mpq，最多10个，空格分开，可以带路径，路径有空格要用双引号，比如：-mpq Language_CHI\\CHI.mpq\r\n"
"-plugin 指定额外的dll，最多10个，空格分开，可以带路径，路径有空格要用双引号，比如：-plugin d2hackmap\\d2hackmap.dll。另外，可以追加plugin的初始化函数，比如：-plugin PlugY.dll:_Init@4\r\n"
"-mpqpath 指定10 mpqs的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠\"\\\\\"，比如：-mpqpath \"d:\\Diablo II D2SE\\r\n"
"-dllpath 追加dll的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠\"\\\\\"，比如：-dllpath \"d:\\Diablo II D2SE\" \"d:\\Diablo II D2SE\\D2SE\\CORES\\1.13c\\r\n"
"-depfix 跳过战网版本检查，去除dep机制\r\n"
"-noborder 窗口无边框\r\n"
"-multiopen 多开\r\n"
"-modpath 自动替代注册表里的InstallPath和Save Path参数，重定向到modpath下面\r\n"
"-nodide 窗口不失去焦点，用于一键跟随，需要打开hackmap的Out Town Select Toggle，否则城外无法选中其他角色。使用方法：\r\n"
"1.人物A队长，出城。\r\n"
"2.人物B出城，左键点击人物A，鼠标别动，点住人物A，拖动鼠标到游戏窗口外。\r\n"
"3.鼠标别回到跟随人物窗口操作。\r\n"
"4.鼠标转移到人物A游戏窗口，操作人物A，其他人物自动跟随成功。\r\n"
"备注：普通攻击模式点击\r\n"
"-nosleep\r\n"
"-opengl/-d3d/-per/-lq/-gamma/-vsync/-fr/-joinid/-gamename/-bn/-mcpip/-nopk/-openc/-arena/-txt/-direct/-ama/-pal/-sor/-nec/-bar/-dru/-asn/-i/-bnacct/-bnpass/-name/-realm/-ctemp/-nm/-m/-minfo/-md/-unique/-act/-log/-msglog/-safe/-seed/-cheats/-ns/-nosound/-questall/-npl/-lem/-nocompress/-gamepass/-skiptobnet/-client/-server/-launch/-title/-notitle/-res800/-res640 都是原版参数，代码里有部分说明，自己摸索吧\r\n");
#endif
}

static void print_help2()
{
/*
    printf("作者：Anders 2021.5.7"
"1、一键安装、一键卸载，因为原版的Storm.dll众所周知的校验，所以要使用自定义loader的话，需要先运行D2LoaderInstall.exe对Storm.dll进行patch，不需要的时候可以用D2LoaderRestore.exe进行恢复。如果用我打包的storm.dll，就不需要D2LoaderInstall.exe和D2LoaderRestore.exe了。\r\n"
"2、支持原版game.exe的所有命令行参数，比如-w、-direct、-txt、-glide、-3dfx、-locale、-title等。\r\n"
"3、兼容v1.09d/1.10f/1.11b/1.12a/1.13c/1.13d。\r\n"
"4、默认根据同目录下的game.exe来判断版本号，也可以用参数-ver 1.13c来指定游戏版本号。\r\n"
"5、支持单人、TCP/IP、战网。\r\n"
"6、参数说明：\r\n"
"-ver 指定游戏版本号，比如-ver 1.13d\r\n"
"-xp 自动设置XP兼容，不需要在exe上右键设置了\r\n"
"-console 开启console窗口，看到输出信息\r\n"
"-hack 类似d2gs的动态patch，其中d2hack.script文件名可以任意替换成自己的，具体的文件格式可以参考Install目录下的例子，已经实现了1.13c的一拳满级和一元购物\r\n"
"-hackpre 这个参数指定的script会在其他插件之前加载，而-hack是在其他插件之后加载，适配多种情况\r\n"
"-glide/-3dfx 3dfx模式\r\n"
"-w -direct -txt 不用介绍了\r\n"
"-title 设定游戏窗口标题\r\n"
"-notitle 游戏窗口不显示任何标题\r\n"
"-ns -nosound 无声模式\r\n"
"-res800 -res640 设定游戏分辨率\r\n"
"-locale 比如-locale CHI，则自动加载Language_CHI\\\\CHI.mpq\r\n"
"-mpq 指定额外的mpq，最多10个，空格分开，可以带路径，路径有空格要用双引号，比如：-mpq Language_CHI\\CHI.mpq\r\n"
"-plugin 指定额外的dll，最多10个，空格分开，可以带路径，路径有空格要用双引号，比如：-plugin d2hackmap\\d2hackmap.dll。另外，可以追加plugin的初始化函数，比如：-plugin PlugY.dll:_Init@4\r\n"
"-mpqpath 指定10 mpqs的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠\"\\\\\"，比如：-mpqpath \"d:\\Diablo II D2SE\\r\n"
"-dllpath 追加dll的加载路径，路径有空格要用双引号，路径的最后不要写反斜杠\"\\\\\"，比如：-dllpath \"d:\\Diablo II D2SE\" \"d:\\Diablo II D2SE\\D2SE\\CORES\\1.13c\\r\n"
"-depfix 跳过战网版本检查，去除dep机制\r\n"
"-noborder 窗口无边框\r\n"
"-multiopen 多开\r\n"
"-modpath 自动替代注册表里的InstallPath和Save Path参数，重定向到modpath下面\r\n"
"-nodide 窗口不失去焦点，用于一键跟随，需要打开hackmap的Out Town Select Toggle，否则城外无法选中其他角色。使用方法：\r\n"
"1.人物A队长，出城。\r\n"
"2.人物B出城，左键点击人物A，鼠标别动，点住人物A，拖动鼠标到游戏窗口外。\r\n"
"3.鼠标别回到跟随人物窗口操作。\r\n"
"4.鼠标转移到人物A游戏窗口，操作人物A，其他人物自动跟随成功。\r\n"
"备注：普通攻击模式点击\r\n"
"-nosleep\r\n"
"-opengl/-d3d/-per/-lq/-gamma/-vsync/-fr/-joinid/-gamename/-bn/-mcpip/-nopk/-openc/-arena/-txt/-direct/-ama/-pal/-sor/-nec/-bar/-dru/-asn/-i/-bnacct/-bnpass/-name/-realm/-ctemp/-nm/-m/-minfo/-md/-unique/-act/-log/-msglog/-safe/-seed/-cheats/-ns/-nosound/-questall/-npl/-lem/-nocompress/-gamepass/-skiptobnet/-client/-server/-launch/-title/-notitle/-res800/-res640 都是原版参数，代码里有部分说明，自己摸索吧\r\n");
*/
}

static BOOL EnableDebugPrivilege(BOOL fEnable)
{ 
    BOOL fOk = FALSE;  
    HANDLE hToken;

    // 得到进程的访问令牌
    if ( OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) )
    {   
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount = 1;
        // 查看系统特权值并返回一个LUID结构体
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
        tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
        // 启用/关闭 特权
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
        fOk = (GetLastError() == ERROR_SUCCESS);
        CloseHandle(hToken);
    }
    else
    {
        return 0;
    }
    return(fOk);
}

//获取对应进程名的ID
static DWORD GetSpecifiedProcessId(const char *pszProcessName)
{
    DWORD id = 0;
    //获得系统快照句柄 (通俗的讲, 就是得到当前的所有进程)
    HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if ( hSnapShot == INVALID_HANDLE_VALUE )
    {
        return FALSE;
    }

    PROCESSENTRY32 pInfo; //用于保存进程信息的一个数据结构
    memset(&pInfo, 0, sizeof(PROCESSENTRY32));
    pInfo.dwSize = sizeof(PROCESSENTRY32);
    //从快照中获取进程列表
    Process32First(hSnapShot, &pInfo); //从第一个进程开始循环，不明觉厉堆栈溢出，搞不懂，不用了
    do
    {
        //这里的 pszProcessName 为你的进程名称
        if(_stricmp(pInfo.szExeFile, pszProcessName) == 0)
        {
            id = pInfo.th32ProcessID ;
            break ;
        }
    } while(Process32Next(hSnapShot, &pInfo) != FALSE);
    CloseHandle(hSnapShot);
    return id; //id 就是你要的进程PID 了..
}

/*
https://d2mods.info/forum/viewtopic.php?t=62807

BBC8 sizeof=0x5C 57entries

struct D2CMDParse
{
	char szSectionName[27];	//+00
	char szKeyName[27];	//+1B
	char szCMDOption[27];	//+36
	BYTE nINIType;		//+51
	BYTE _1[2];		//+52
	int nOffset;		//+54
	int nDefaultValue;	//+58
}

fog.10021
fog.10019
D2Common.10097 InitIgnoreList
fog.10101
fog.10089
fog.10082 - null
fog.10218

d2win.10174

D2Win.10072(D2Win.10152,D2Win.10140)

fog.10227

d2win.10058
d2win.10071
if(pCMD+a) d2gfx.10068
d2win.10129
d2gfx.10050
d2gfx.10007 D2GetWindowHandle
d2gfx.10008
d2gfx.10071
d2gfx.10065
d2gfx.10049 D2SetFixedAspectRatio
d2sound.10023
{
d2sound.10024
d2win.10132
d2gfx.10050
}

d2sound.10024
d2win.10132
d2gfx.10050
d2win.10058
fog.10090
D2MCPClient.10006
pCMD+10 -> ()
D2Common.10925 D2CloseIgnoreList
Fog.10143

int __fastcall INIT_StartGame(HINSTANCE hInstance, D2CmdLineArgStrc* pCMD)
{
    //fog.10021
	FOG_InitLogManager(gpLogPath);
    
    //fog.10019
	FOG_InitSystem("Diablo II", NULL, "v1.xy", 1);

#if ( __D2_EVENT_OK__ )
	HANDLE hEvent = OpenEvent(2, TRUE, "DIABLO_II_OK");
	SetEvent(hEvent);
	CloseHandle(hEvent);
#endif

	if (gdwVersion == VER_113D)
        //D2Common.10097 InitIgnoreList
		D2COMMON_InitIgnoreList();

    //fog.10101
	FOG_SetWorkingDirectory(pCMD->bDirect, 0);

    //fog.10089
	FOG_InitAsyncData(1, 0);

    //fog.10082 - null  ????
    //fog.10218
	FOG_InitMaskTables();
	
    //d2win.10174
    BOOL bReturn = D2WIN_LoadArchives();
	if (bReturn)
	{
        //D2Win.10072(D2Win.10152,D2Win.10140)
		if (bReturn = D2WIN_LoadExpansionArchives(D2WIN_PlayDiscMessage, D2WIN_ExpansionDiscMessage, 0, pCMD))
		{
            //fog.10227
			pCMD->dwExpansion = FOG_CheckExpansion();
			DWORD dwRenderMode = RENDER_NONE;
			if (pCMD->b3DFX)
				dwRenderMode = RENDER_GLIDE;
			else if (pCMD->bWindowed)
				dwRenderMode = RENDER_GDI;
			else
				dwRenderMode = (pCMD->bD3D) ? RENDER_DIRECT3D : RENDER_DIRECTDRAW;

            //d2win.10071
			if (!D2WIN_CreateWindow(hInstance, dwRenderMode, pCMD->bWindowed, !pCMD->bNoCompress))
			{
				ERROR_Raise("Failed To Initialize Window", FALSE);
				return -1;
			}

			if (pCMD->bPerspective && dwRenderMode >= RENDER_GLIDE)
                //d2gfx.10068
				D2GFX_SetPerspectiveMode(TRUE);

            //d2win.10129
			if (!D2WIN_InitSpriteCache(pCMD->bWindowed, (pCMD->bWindowed) ? 2 : 0))	// the last param is the start up resolution :)
			{
                //d2gfx.10050
				D2GFX_DestroyWindow();
				ERROR_Raise("Failed To Initialize Video", FALSE);
				return -1;
			}
            
            //d2gfx.10007 D2GetWindowHandle

			if (pCMD->bLowEnd)
                //d2gfx.10008
				D2GFX_EnableLowQuality();

			if (pCMD->dwGamma != 0)
                //d2gfx.10071
				D2GFX_SetGamma(pCMD->dwGamma);

			if (pCMD->bVSync)
                //d2gfx.10065
				D2GFX_EnableVSync();

			if (gdwVersion >= VERSION_113C)
			{
				if(((bool*)pCMD)[5])//if (pCMD->bFixedAspectRatio)
                    //d2gfx.10049 D2SetFixedAspectRatio
					D2GFX_SetFixedAspectRatio();
			}

			BOOL bCloseSound = FALSE;
			if (!pCMD->bNoSound)
			{
                //d2sound.10023
				D2SOUND_InitSoundSystem(pCMD->dwExpansion);
				bCloseSound = TRUE;
			}

			INIT_CacheInterfaces();
			int nInterface = 4;
			while (nInterface = INIT_Interface(nInterface, pCMD));

			if (bCloseSound)
                //d2sound.10024
				D2SOUND_CloseSoundSystem();

            //d2win.10132
			D2WIN_CloseSpriteCache();
            //d2gfx.10050
			D2GFX_DestroyWindow();
            //d2win.10058
			D2WIN_UnloadArchives();
            //fog.10090
			FOG_CloseAsyncData();
            //D2MCPClient.10006
			D2MCPCLIENT_CloseMCP();
			D2BNInterfaceStrc* pInterface = INIT_GetInterface(pCMD, gdwVersion);
			if (pInterface != NULL)
				pInterface->pfShutdown();
if (gdwVersion == VER_113D)
                //D2Common.10925 D2CloseIgnoreList
				D2COMMON_InitIgnoreList();

            //Fog.10143
			FOG_CloseSystem(NULL);
		}
	}
	else
	{
        //d2win.10058
		D2WIN_UnloadArchives();
		ERROR_Raise("Failed To Load Archives", FALSE);
		return -1;
	}

	return (bReturn) ? 0 : -1;
}
*/

int main(int argc, char** argv)
{
    //printf("%d %X\r\n", sizeof(ST_CLIENT_DATA), sizeof(ST_CLIENT_DATA));
    static ST_CLIENT_DATA stClientData;
    char acBuffer[D2LOADER_MAXPATH] = { 0 };
    ST_LAUNCH_CALLBACK *pstCallBack = NULL;
    DWORD dwGameMode = launcher;
    DWORD dwVideoMode;
    int iFixedAspectRatio;
    BOOL boolInitGfx = FALSE, boolInitSound = FALSE, boolLoadMpqs = FALSE;
    HANDLE hLanguageMPQ = NULL;
    DWORD i;
    int iVer1 = 0, iVer2 = 0;
    char cVer3 = 'x';
    HANDLE ahExtendMPQ[MAX_EXTEND_MPQ] = {NULL};
    HANDLE event;
    char *pcTemp = NULL;

    GetModuleFileName(NULL, m_acRunningPath, sizeof(m_acRunningPath)); 
    pcTemp = strrchr(m_acRunningPath, '\\');
    if ( NULL != pcTemp )
    {
        *pcTemp = 0;
    }

    memset(&stClientData, 0, sizeof(stClientData));

    if ( 1 >= argc )
    {
        if ( TRUE != parse_ini(&stClientData) )
        {
            goto out3;
        }
    }
    else
    {
        parse_command(argc, argv, &stClientData);
    }

    if ( TRUE == m_boolWithConsole && TRUE == AllocConsole() )
    {
        SetConsoleTitle("D2Loader Console");
        freopen("conin$", "r+t", stdin);
        freopen("conout$", "w+t", stdout);
        freopen("conout$", "w+t", stderr);
        printf("D2Loader console start！\r\n");
    }

    if ( TRUE == m_boolHelpMode )
    {
        print_help();
        printf("Press Enter to quit。\r\n");
        getchar();
        goto out2;
    }

    if ( TRUE == m_boolCheckStruct )
    {
        D2Loader_CheckStruct();
        printf("Press Enter to quit。\r\n");
        getchar();
        goto out2;
    }

    printf("working path:%s\r\n", m_acRunningPath);

    if ( 0 != m_acModPath[0] )
    {
        printf("mod path:%s\r\n", m_acModPath);
        SetCurrentDirectoryA(m_acModPath);
    }
    else
    {
        printf("running path:%s\r\n", m_acRunningPath);
        SetCurrentDirectoryA(m_acRunningPath);
    }
#if 0
    //以当前目录为准，忽略注册表
    memset(acBuffer, 0, sizeof(acBuffer));
    m_pfnStormRegLoadString("Diablo II", "InstallPath", 0, acBuffer, sizeof(acBuffer) / sizeof(acBuffer[0]));
    SetCurrentDirectoryA(acBuffer);
#endif

    D2Loader_PatchXpCompatible();

    if ( 0 > m_iGameVersion )
    {
        m_iGameVersion = GetD2Version("Game.exe");
    }
    if ( V109d != m_iGameVersion && V110f != m_iGameVersion && V111b != m_iGameVersion && V112a != m_iGameVersion && V113c != m_iGameVersion && V113d != m_iGameVersion )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION,
            "Current version of LoD (%s) isn't compatible with D2Loader.\n\n"
            "Please, install a patch between 1.09d/1.10f/1.11b/1.12a/1.13c/1.13d.",
            GetVersionString(m_iGameVersion));
        goto out2;
    }

    event = OpenEventA(2, D2TRUE, "DIABLO_II_OK");
    if ( event )
    {
        SetEvent(event);
        CloseHandle(event);
    }

    D2Loader_InitClientData(&stClientData);

    stClientData.mpq_callback = Proc_new;

    if ( stClientData.glide_mode )
    {
        dwVideoMode = glide;
    }
    else if ( stClientData.window_mode )
    {
        dwVideoMode = gdi;
    }
    else
    {
        dwVideoMode = (stClientData.d3d_mode ? d3d : ddraw);
    }

    LoadAllLibraries(&stClientData, dwVideoMode);
#if 0
    if ( TRUE == m_boolWithConsole )
    {
        D2Loader_CheckDllImageBase();
    }
#endif

    D2Loader_PatchHackScript(m_acHackScriptPre0); //避免无规则加载dll，所以由loader统一加载完成后，再打用户补丁
    D2Loader_PatchHackScript(m_acHackScriptPre); //避免无规则加载dll，所以由loader统一加载完成后，再打用户补丁

    if ( TRUE != D2Loader_InitFuncPtr() )
    {
        goto out2;
    }

    D2Loader_PatchModPath();
    D2Loader_PatchMpqPath();
    D2Loader_PatchDepFix();
    D2Loader_PatchMultiOpen();
    D2Loader_PatchNoSleep();

    memset(acBuffer, 0, sizeof(acBuffer));

    switch ( m_iGameVersion )
    {
        case V109d:
            iVer1 = 1;
            iVer2 = 9;
            cVer3 = 'd';
            break;

        case V110f:
            iVer1 = 1;
            iVer2 = 10;
            cVer3 = 'f';
            break;

        case V111b:
            iVer1 = 1;
            iVer2 = 11;
            cVer3 = 'b';
            break;

        case V112a:
            iVer1 = 1;
            iVer2 = 12;
            cVer3 = 'a';
            break;

        case V113c:
            iVer1 = 1;
            iVer2 = 13;
            cVer3 = 'c';
            break;

        case V113d:
            iVer1 = 1;
            iVer2 = 13;
            cVer3 = 'd';
            break;

        default:
            break;
    }

    memset(acBuffer, 0, sizeof(acBuffer));
    if ( 0 != m_acModPath[0] )
    {
        if ( !_stricmp(m_acModPath, m_acRunningPath) )
        {
            //nothing
        }
        else if ( NULL == strstr(m_acModPath, ":\\") )
        {
            //指定了相对路径的modpath，重定向log路径
            sprintf_s(acBuffer, "%s\\D2MC", m_acModPath);
        }
        else if ( NULL != strstr(m_acModPath, m_acRunningPath) )
        {
            //指定了绝对路径并且跟runningpath有重合，也就是D2ModCenter调用
            sprintf_s(acBuffer, "%s\\D2MC", &m_acModPath[strlen(m_acRunningPath) + 1]);
        }
    }

    if ( 0 == acBuffer[0] )
    {
        strcpy_s(acBuffer, "D2MC");
    }

    printf("log prefix: %s\r\n", acBuffer);
    m_pfnFogSetLogPrefix(acBuffer);

    memset(acBuffer, 0, sizeof(acBuffer));
    if ( 0 >= m_pfnStormSprintf(acBuffer, sizeof(acBuffer) / sizeof(acBuffer[0]), "v%d.%02d", iVer1, iVer2) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Call Storm.Sprintf failed!");
        goto out3;
    }
    m_pfnFogSetErrorHandler("Diablo II", D2Critical_Callback, acBuffer, D2TRUE);

    if ( NULL != m_pfnD2Common10097 )
    {
        m_pfnD2Common10097();
    }

    m_pfnFogSetFileOptions(stClientData.direct, D2FALSE);
    m_pfnFogSetAsyncData(D2TRUE, D2FALSE);
    m_pfnD2Fog10082();
    m_pfnFogInit();
    boolInitGfx = TRUE;

    printf("Loading MPQs...\r\n");
    if ( !m_pfnD2WinLoadMPQs() )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load MPQs failed!");
        goto out3;
    }
    boolLoadMpqs = TRUE;
    D2Loader_PutClientData(&stClientData);
    if ( !m_pfnD2WinLoadExpansionMPQs(D2WIN_PlayDiscMessage, D2WIN_ExpansionDiscMessage, 0, m_pvClientData) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load Expansion MPQs failed!");
        goto out3;
    }
    D2Loader_GetClientData(&stClientData);
    stClientData.expansion = m_pfnFogIsExpansion();

    if ( 0 != m_acLanguageMpq[0] && !m_pfnStormOpenArchive(m_acLanguageMpq, 0x1B58, 2, &hLanguageMPQ) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load Language MPQ failed!");
        goto out3;
    }

    for ( i = 0; i < m_dwExtendMpq; ++i )
    {
        if ( !m_pfnStormOpenArchive(m_aacExtendMpq[i], 0x1B58, 2, &ahExtendMPQ[i]) )
        {
#if 0
            ahExtendMPQ[i] = NULL;
            continue;   //用户自定义的mpq找不到的话，改为直接跳过
#else
            //还是提示一下好点吧
            msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Load MPQ %s failed!", m_aacExtendMpq[i]);
            goto out3;
#endif
        }
    }

    printf("Initing GFX...\r\n");
    if ( !m_pfnD2WinInitGfx(GetModuleHandle(NULL), dwVideoMode, stClientData.window_mode, !stClientData.no_gfx_compress) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Init GFX failed!");
        goto out3;
    }

    if ( stClientData.perspective && dwVideoMode >= glide )
    {
        m_pfnD2GfxSetPerspective(D2TRUE);
    }

    printf("Creating a window...\r\n");
    if ( !m_pfnD2WinCreateWindow(stClientData.window_mode, (stClientData.window_mode) ? res_800x600 : res_640x480) )
    {
        msgBox(m_pcBoxName, MB_OK | MB_ICONEXCLAMATION, "Create window failed!");
        goto out3;
    }

    m_LoaderWndHandle = m_pfnGetHwnd();

    if ( 0 != m_acGameTitle[0] )
    {
        if ( !_stricmp("notitle", m_acGameTitle) )
        {
            SetWindowText(m_LoaderWndHandle, "");
        }
        else
        {
            SetWindowText(m_LoaderWndHandle, m_acGameTitle);
        }
    }

    if ( TRUE == m_boolNoBorder )
    {
        SetWindowLong(m_LoaderWndHandle, GWL_STYLE, (GetWindowLong(m_LoaderWndHandle, GWL_STYLE) | WS_POPUP) & ~WS_CAPTION); 
    }

    if ( TRUE == m_boolNoHide )
    {
        m_OldLongWinProc = (WNDPROC)SetWindowLong(m_LoaderWndHandle, GWL_WNDPROC, (LONG)D2Loader_NoHideWndProc);
    }

    if ( stClientData.low_quality )
    {
        m_pfnD2GfxSetLowQuality();
    }
    if ( stClientData.gamma )
    {
        m_pfnD2GfxSetGamma(stClientData.gamma);
    }
    if ( stClientData.vsync )
    {
        m_pfnD2GfxSetEnableVsync();
    }
    iFixedAspectRatio = 1;
    if ( NULL != m_pfnStormRegLoadValue )
    {
        m_pfnStormRegLoadValue("Diablo II", "Fixed Aspect Ratio", 0, &iFixedAspectRatio);
    }
    if ( NULL != m_pfnD2GfxFixAspectRatio && (stClientData.fix_aspect_ratio || iFixedAspectRatio != 1) )
    {
        m_pfnD2GfxFixAspectRatio();
    }
    if ( !stClientData.expansion )
    {
        m_pfnStormSetResolution("Diablo II", "Resolution", 0, 0);
    }
    else if ( 0 <= m_iSpecRes )
    {
        m_pfnStormSetResolution("Diablo II", "Resolution", m_iSpecRes, m_iSpecRes);
    }

    if ( !stClientData.no_sound )
    {
        printf("Initing sound system...\r\n");
        m_pfnD2SoundInit(stClientData.expansion, D2FALSE);
        boolInitSound = TRUE;
    }

    switch ( m_iGameVersion )
    {
        case V109d:
        case V110f:
        case V111b:
        case V112a:
        case V113c:
        case V113d:
            m_pfnD2LaunchGetCb = (type_pfnD2LaunchGetCb)GetDllOffset2("D2Launch.dll", "QueryInterface");
            m_pfnD2ClientGetCb = (type_pfnD2LaunchGetCb)GetDllOffset2("D2Client.dll", "QueryInterface");
            m_pfnD2MultiGetCb = (type_pfnD2LaunchGetCb)GetDllOffset2("D2Multi.dll", "QueryInterface");
            break;

        default:
            break;
    }

    for ( i = 0; i < m_dwExtendPlugin; ++i )
    {
        D2Loader_LoadLibrary(m_aacExtendPlugin[i]);
    }

    D2Loader_PatchHackScript(m_acHackScript0);
    D2Loader_PatchHackScript(m_acHackScript);

    if ( TRUE == m_boolWithConsole )
    {
        PrintAllDllInfo();
    }

    D2Loader_PutClientData(&stClientData);
    while ( TRUE )
    {
        printf("Entering the game mode: %d\r\n", dwGameMode);

        switch ( dwGameMode )
        {
            case launcher:
                pstCallBack = m_pfnD2LaunchGetCb();
                break;

            case client:
                pstCallBack = m_pfnD2ClientGetCb();
                break;

            case multiplayer:
                pstCallBack = m_pfnD2MultiGetCb();
                break;

            default:
                pstCallBack = NULL;
                break;
        }

        if ( NULL == pstCallBack )
        {
            break;
        }
        dwGameMode = pstCallBack->pfnLaunch(m_pvClientData);
    }

    goto out;

out3:
    if ( TRUE == m_boolWithConsole )
    {
        PrintAllDllInfo();
        printf("Press return key to quit game!\r\n");
        getchar();
    }

out:
    if ( !stClientData.no_sound && boolInitSound )
    {
        printf("Shutting down the sound system...\r\n");
        m_pfnD2SoundShutdown();
    }
    if ( boolInitGfx )
    {
        printf("Shutting down the GFX...\r\n");
        m_pfnD2WinDeinitGFX();
        m_pfnD2GfxRelease();
    }
    m_pfnD2WinUninit();
    m_pfnFogFreeAsyncData();
    m_pfnD2MCPClientUninit();
#if 0
    v14 = *(_DWORD *)(v3 + 545);
    if ( v14 )
    {
        (*(void (**)(void))(v14 + 16))();
    }
#endif
    if ( NULL != m_pfnD2Common10925 )
    {
        m_pfnD2Common10925();
    }
    if ( hLanguageMPQ )
    {
        m_pfnStormCloseArchive(hLanguageMPQ);
    }
    for ( i = 0; i < m_dwExtendMpq; ++i )
    {
        if ( ahExtendMPQ[i] )
        {
            m_pfnStormCloseArchive(ahExtendMPQ[i]);
        }
    }
    if ( boolLoadMpqs && NULL != m_pfnD2WinUnloadMPQs )
    {
        printf("Unloading the MPQs...\r\n");
        m_pfnD2WinUnloadMPQs();
    }
    m_pfnFreePools(NULL);

    D2Loader_FreeClientData(&stClientData);

out2:
    if ( TRUE == m_boolWithConsole )
    {
        fclose(stderr);
        fclose(stdout);
        fclose(stdin);
        FreeConsole();
    }

    return 0;
}

